A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Tutoriais e Programação

Applet Espelhando Imagens

Sex

10

Abr

2009


21:41

(4 votos, média 5.00 de 5) 


Nível intermediário

O espelhamento de imagens é um efeito interessante e muito fácil de ser obtido quando as ferramentas certas são utilizadas. Neste caso, o conhecimento e o domínio do método copyArea é essencial para obter o resultado desejado, portanto, a leitura de Os segredos de copyArea é altamente recomendada.

Objetivo do projeto

  1. Criar e controlar threads
  2. Entender a estrutura de matrizes
  3. Trabalhar com a interface gráfica
  4. Trabalhar com o MediaTracker
  5. Criar mensagens na barra de status
  6. Criar efeitos gráficos interessantes

O código Java

import java.applet.*; import java.awt.*; public class espelhoImg extends Applet implements Runnable { Thread linha = null; boolean vivo = true; Image origImg = null; int larg = 0, alt = 0; public void init() { String s = getParameter("imagem"); if (s != null) { Image img = getImage(getCodeBase(), s); if (img != null) { MediaTracker mt = new MediaTracker(this); mt.addImage(img, 0); try { mt.waitForID(0); } catch(InterruptedException _ex) { } larg = img.getWidth(this); alt = img.getHeight(this); origImg = createImage(larg << 1, alt); Graphics g = origImg.getGraphics(); g.drawImage(img, 0, 0, this); } } } public void start() { if (linha == null && origImg != null) { linha = new Thread(this); linha.start(); } vivo = (origImg != null); } public void run() { while (vivo) { Graphics g = origImg.getGraphics(); g.setColor(Color.white); g.fillRect(larg, 0, larg, alt); for (int i = 0; i < larg; i++) { g.copyArea(i, 0, 1, alt, (larg << 1) - i * 2, 0); showStatus("copia " + i + " para " + ((larg << 1) - i)); repaint(); try { linha.sleep(100); } catch(InterruptedException _ex) { } } try { linha.sleep(1000); } catch(InterruptedException _ex) { } } } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if(origImg != null) g.drawImage(origImg, 0, 0, this); } public void stop() { vivo = false; } } // fim da classe

O applet funcionando

O código Java foi compilado para um arquivo de bytecodes de nome espelhoImg.class. Crie um documento HTML e insira este applet. A altura e a largura do applet devem ser a altura e a largura da imagem que deve ser espelhada. O resultado é o seguinte:

info Aproveite e dê uma olhada no rodapé do seu browser. Lá você poderá ver a sequência da cópia das colunas de pixels.

Explicações que podem ser úteis

Há algumas novidades neste applet além do uso do método copyArea: usamos a interface gráfica de um objeto Image para atualizá-la antes de mostrá-la na janela do applet, um objeto MediaTracker para monitorar a leitura de uma imagem e usamos operadores de deslocamento bit-a-bit.

A declaração da classe

Precisamos apenas dos pacotes java.applet.* e java.awt.*. Este applet também é Runnable, quer dizer, o applet vai conter um método run que irá gerenciar o seu comportamento. Para isto criamos um thread adicional. Se você ainda não leu O que são threads e como domá-los, aproveite a oportunidade agora. Neste ponto, o tutorial Brincando com Cores também é uma boa pedida. O thread para controlar o comportamento do applet é declarado como Thread linha = null e boolean vivo = true o acompanha.

Além disto, declaramos um objeto Image origImg = null e int larg (largura da imagem) e int alt (altura da imagem).

O método init()

No método init() lemos o parâmetro "imagem" e atribuímos seu valor à variável local String s. Se s não for null, temos o nome do arquivo que contém a imagem desejada. Instanciamos então o objeto Image img, o qual conterá a imagem gráfica. Se o arquivo mencionado não existir, img será igual a null e as linhas seguintes não serão executadas.

De posse da imagem, criamos o objeto MediaTracker mt (do pacote AWT). O MediaTracker permite monitorar e controlar o carregamento de objetos de mídia (imagens e sons), mas, atualmente, o MediaTracker rastreia apenas os objetos Image. Adicionamos o objeto Image img ao nosso rastreador usando seu método addImage - mt.addImage(img, 0). O método exige dois parâmetros: a imagem que deve ser monitorada e um número de identidade (id). Este id é qualquer número inteiro e poderá ser utilizado para obter informações da imagem correspondente. Podemos adicionar quantas imagens quisermos ao MediaTracker e, desta forma, monitorar seu pre-carregamento. Se atribuirmos o mesmo id a um grupo de imagens, podemos obter informações sobre o grupo todo. Os métodos waitForAll (espere por todas) e waitForID (espere pelo id), também do MediaTracker, retornarão o controle ao chamador apenas quando a(s) imagen(s) esteja(m) carregada(s). No nosso applet utilizamos mt.waitForID(0) dentro de um bloco try/catch.

Com a imagem em mãos, atribuímos à variável larg a largura da imagem e à alt a sua altura. Em seguida criamos o objeto Image origImg com o dobro da largura da imagem enviada como parâmetro - origImg = createImage(larg << 1, alt) (veja o operador << em "Funções e Operadores Matemáticos"). Em seguida, obtemos a interface gráfica Graphics g do objeto Image recém-criado (Graphics g = origImg.getGraphics()) e aplicamos a ela a imagem do nosso gráfico a partir das coordenadas 0,0 (g.drawImage(img, 0, 0, this)).

Com o applet inicializado, está tudo pronto para o método start.

O método start()

No método start() criamos nossa linha de execução, a Thread linha. A esta altura já sabemos que o método start() é executado toda vez que a página que contém o applet se tornar ativa no browser. Portanto, se a linha de execução não existir e se a imagem origImg foi obtida no método init (if (linha == null && origImg != null) ), criamos uma nova (linha = new Thread(this) - "this" significa "este applet") e a tornamos ativa com linha.start(). A seguir, atribuímos à variável vivo o valor true (vivo = true;), a qual indica que o thread se encontra no estado ativo (executável).

O método run()

Se o applet implementa a interface Runnable e já possui um thread adicional, a presença de um método run() é obrigatória, porém o método run() só atua se o thread de execução estiver ativo (while(vivo)), e vivo só é verdadeiro se a página que contém o applet estiver ativa. Caso o usuário mude de página, o thread é desativado pela Java, assim como todos os procedimentos a ele associados. Não sobra "lixo" e os recursos da máquina do usuário são liberados. É claro que nossa variável vivo precisa indicar esta nova situação. Como sabemos que o método stop() é chamado quando o thread "morre", é neste ponto que atualizamos nossa variável.

A primeira coisa que o método run() faz é obter a interface gráfica do objeto Image origImg com Graphics g = origImg.getGraphics(). Esta interface gráfica é uma tela de pixels com tantas linhas quanto for a altura e tantas colunas quanto for a largura da imagem. No exemplo, temos 80 linhas e 240 colunas. Não podemos esquecer que as coordenadas desta tela se iniciam em x = 0 e y = 0. Isto significa que as coordenadas das linhas vão de 0 a 79 (80 linhas) e as das colunas vão de 0 a 239 (240 colunas). Portanto, quando quiser se referir à 10a. linha, por exemplo, sua coordenada y é igual a 9 e que sua 58a. coluna tem x = 57.

Na inicialização do applet colocamos na metade da esquerda da interface gráfica g imagem origImg. A metade da direita está livre e é nela que inicialmente desenhamos um retângulo preenchido de branco: g.setColor(Color.white) e g.fillRect(larg, 0, larg, alt). As coordenadas da imagem na metade esquerda são 0,0 até 119,80. O retângulo branco é desenhado a partir de 120, 0 com uma largura (x) de 120 e altura (y) de 80. Portanto, ocupa toda a metade direita da interface gráfica.

Tendo preparado a interface gráfica para receber nossa imagem espelhada, iniciamos um loop com valores de i que vão de 0 a 119, que são as coordenadas x da imagem original. Dentro deste loop colocamos o método copyArea, que fará todo o trabalho de espelhamento: g.copyArea(i, 0, (larg << 1) - i * 2, 0). O formato de copyArea é o seguinte:

copyArea(int x, int y, int largura, int altura, int desloca_x, int desloca_y)

onde x e y são as coordenadas do ponto de origem (onde começa o retângulo que deve ser copiado); int largura e int altura são a largura e a altura do retângulo a ser copiado; int desloca_x é o deslocamento do retângulo copiado no eixo x a partir das coordenadas do ponto de origem; int desloca_y é o deslocamento do retângulo copiado no eixo y a partir das coordenadas do ponto de origem.

Copiamos a imagem original em "fatias", coluna por coluna, e transportamos a cópia do fim para o começo da metade direita do ambiente gráfico. Veja o exemplo abaixo:

  • Quando copiamos o retângulo com as coordenadas de origem x = 0 e y = 0, largura 1 e altura 5, e o deslocamos para a 10a. coluna, o deslocamento de x é 10 (pois 0 + 10 = 10).
  • Quando copiamos o retângulo com as coordenadas de origem x = 1 e y = 0, largura 1 e altura 5, e o deslocamos para a 9a. coluna, o deslocamento de x é 8 (pois 1 + 8 = 9), e assim sucessivamente.

A fórmula que calcula o deslocamento é largura total - 2 * coluna, ou seja,

desloca0 = 10 - 2 * 0 = 10
desloca1 = 10 - 2 * 1 = 8
 ...
desloca4 = 10 - 2 * 4 = 2
10
  8  
    6    
      4      
        2        
0,0 1,0 2,0 3,0 4,0 5,0 6,0 7,0 8,0 9,0 10,0

No nosso applet, o cálculo (larg << 1) - i * 2 indica o deslocamento da coluna i. A cada cópia, a barra de status indica qual coluna foi copiada para onde e solicitamos que a imagem recém modificada seja mostrada na tela do applet. Para isto, basta utilizar o método showStatus("mensagem") e o método repaint(), o qual acionará os métodos update e paint. Precisamos de repaint porque o ambiente gráfico de origImg é apenas um buffer de trabalho que só é mostrado na tela do applet quando o copiamos para o ambiente gráfico da tela do applet.

Como o procedimento de copiar a imagem original em "fatias" e transportá-la para a metade direita é MUITO rápido, precisamos introduzir um tempo de espera através de linha.sleep(100), que faz com que o thread fique "dormindo" 0.1 segundo.

Terminado o loop, toda a imagem à esquerda é transportada "ao contrário" para a direita e o espelhamento horizontal é obtido. Fazemos uma pausa de 1 segundo com linha.sleep(1000) e reiniciamos o processo.

O método update()

O método update(Graphics g) nada mais faz do que chamar o método paint(). Não há necessidade de implementar código por que não precisamos refazer o fundo da nossa imagem, principal função do update.

O método paint()

Neste applet, a única função do método paint(Graphics g) é desenhar na tela do mesmo a imagem origImg, se ela existir. Caso a imagem não tenha sido encontrada e/ou carregada, o método paint não faz nada.

O método stop()

Quando o usuário abandona a página onde está o applet (ou por motivos outros), o método stop() é invocado. No nosso caso, sua única e importantíssima missão é "desligar" nossa linha de execução e deixar que a Java faça a faxina (liberando variáveis, threads ocultos, etc) antes de "matar" o thread que nós criamos... ou seja, não sobra lixo e os recursos da máquina são liberados. Basta dizer que a variável vivo é falsa para que o método run() seja interrompido e para que a Java assuma o comando.

Lição de casa

Modifique e amplie o código deste applet. Sugestões:

  1. Passe parâmetros para controlar as pausas.
  2. Passe um parâmetro para indicar a cor de fundo da metade direita do applet.
  3. Experimente fazer um espelhamento vertical da imagem. É praticamente o mesmo applet, basta alterar as referências no loop for do método run. Se você não conseguir, dê uma olhada em "Reflexo, o espelhamento vertical".

Finalmentes

Ufa!!! Haja explicação! Mas, para o bem de todos e felicidade geral da nação, estes conceitos servirão de base para muitos outros applets, dos mais simples aos mais elaborados.

Este applet é bastante simples, a explicação é que precisou ser longa. O que importa é que o efeito obtido é interessante e pode ser utilizado em applets mais complexos, como aquele do lago... sabe qual é?

Você pode baixar o texto deste tutorial, o código fonte e a classe deste applet na seção de downloads em tutoriais/java.

Grande abraço e sucesso!

vó Vicki vovo

Логофет ВадимКупить веерную кисть полигон купить видеокамерупроизводство автомобиляspanish toадреса никас

Informações adicionais