Baixe o app para aproveitar ainda mais
Prévia do material em texto
Trabalhando com Imagens Escrito por Vinícius Godoy de Mendonça 10 people like this. Be the first of your friends.Like Nos artigos passados, vimos como desenhar imagens através de comandos de pintura. Entretanto, geralmente, não é o programador que desenha imagens, e sim, um artista num software de pintura qualquer. Nesse artigo, vamos ver como trabalhar com essas imagens, gravadas em disco. Carregando imagens O primeiro passo para usar qualquer imagem, obviamente, é carregalas do disco. O Java oferece duas maneiras de se fazer isso, através da carga indireta, assíncrona e através da carga direta, síncrona. Carga assíncrona Com a carga assíncrona, comandamos o Java para que carregue uma imagem. Uma thread secundária é disparada para realizar esse trabalho, fazendo com que o método de leitura retorne imediatamente. Não poderemos usar a imagem logo em seguida, pois ela pode não estar na memória. Para sabermos o momento em que ela foi completamente carregada, temos que registrar umImageObserver, ou utilizar da interface MediaTracker. O código abaixo demonstra um exemplo: Carga síncrona Embora a carga assíncrona seja adequada para aplicações, sobretudo applets, ela não é adequada para jogos. Em quase 100% dos casos, iremos precisar ter certeza de que a imagem foi carregada, antes de exibila. Com a carga síncrona o java carregará uma imagem, e só retornará quando ela estiver integralmente carregada. Fazemos a carga síncrona através da classe ImageIO: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //Disparamos a carga da imagem. O objeto Image representa a imagem sendo carregada. //Esse método retorna imediatamente. Image image = Toolkit.getDefaultTookit().getImage(fileName); //Criamos um MediaTracker para aguardar a carga. O parâmetro this representa um componente, //que pode ser o frame ou o applet onde o tracker está. MediaTracker mt = new MediaTracker(this); //Registramos a imagem com id 0. mt.addImage(image, 0); //Esperamos ela carregar. Poderíamos registrar várias imagens, //e também chamar o método waitForAll(), para carregar todas. try { mt.waitForId(0); } catch (InterruptedException e) { } 1 2 3 4 try { BufferedImage image = ImageIO.read(new File(fileName)); } catch (IOException e) { } ? ? Note que o tipo retornado agora é um BufferedImage, não simplesmente um Image. O BufferedImage é uma classe filha de Image que representa uma imagem carregada na memória. A grande diferença das duas é que o BufferedImage irá permitir a manipulação da imagem. Carregar imagens através da classe ImageIO é a forma recomendada pela Sun atualmente. Desenhando a imagem Você já viu nos artigos anteriores como trabalhar com a classe Graphics. Como você já deve ter deduzido, existem métodos por lá para desenhar a imagem. Uma das coisas mais interessantes, é que o Java não disponibiliza um, mas sim, dezenas de métodos para isso. Veremos dois dos mais importantes. Os outros, são derivações destes. O primeiro, e mais simples, é quando queremos desenhar uma imagem num local específico da tela. Simplesmente passamos a imagem, duas coordenadas e, opcionalmente, um ImageObserver. Essa interface é útil para quando usamos carga assíncrona, e não carregamos a imagem através do media tracker. O método draw() irá esperar até que a imagem esteja desenhada, para então disparar um evento nos avisando que a pintura terminou. Essa técnica é adequada em aplicações, onde esperar pela carga da imagem no momento da pintura não é um problema. Não é o caso dos jogos. Como já teremos carregado nossa imagem com um MediaTracker, ou teremos usado o ImageIO, podemos passar null nesse parâmetro. O segundo método de pintura, permitenos desenhar apenas uma parte da imagem: Nesse método, fornecemos a imagem, 4 coordenadas no destino (dst) e 4 na origem (src). O Java irá pintar só a porção determinada por essas coordenadas, fazendo a escala para que ela encaixe no destino. Por exemplo, se as coordenadas de origem forem (0,0)(10,10) e a de destino forem (0,0)(25,25), a imagem será redimensionada. Esse método é útil para criarmos, por exemplo, uma única imagem com várias partes de uma animação, e desenharmos apenas uma parte por vez no programa. Desenhando sobre uma imagem Um dos recursos mais bacanas da API do Java 2D, é que é extremamente fácil desenhar sobre uma imagem. Todo BufferedImage é também uma área de pintura. Podemos criar um BufferedImage em branco e desenhar sobre ele, ou mesmo desenhar sobre uma imagem carregada. Para isso, usamos o método createGraphics(), que retorna um objeto do tipo Graphics2D. A partir daí, basta desenhar na imagem como faríamos na tela. No artigo anterior, comentei que o desenhar diretamente no Java 2D, sobretudo quando se usa antialiasing e GradientPaint pode ser uma tarefa bastante custosa. Agora, e se fizéssemos esses comandos de desenho uma única vez, e guardássemos o resultado numa BufferedImage? Os desenhos seguintes seriam feitos diretamente através da imagem, sem envolver cálculos de antialising, escala, rotação, preenchimento ou texturas. De fato, fazer isso é possível, e até bastante fácil: 1 2 3 boolean Graphics.drawImage(Image img, int x, int y, ImageObserver observer); 1 2 3 4 boolean Graphics.drawImage(Image img, int dstx1, int dsty1, int dstx2, int dsty2, int srcx1, int srcy1, int srcx2, int srcy2, ImageObserver observer); 1 2 3 4 5 6 7 8 9 10 11 //Criamos uma imagem de 80x100 pixels BufferedImage ghost = new BufferedImage( 80, 100, BufferedImage.TYPE_INT_ARGB); //Obtemos o contexto gráfico dessa imagem Graphics2D g2d = ghost.createGraphics(); //Desenhamos nela o fantasma do artigo anterior :) new Ghost().draw(g2d); //Liberamos o contexto. ? ? ? Após esse código, poderíamos simplesmente desenhar a imagem definida na variável ghost, quantas vezes quiséssemos. Isso permite manter a qualidade do desenho ao mesmo tempo que mantemos a velocidade de pintura na hora do jogo propriamente dito. Salvando a imagem no disco O método ImageIO.write é responsável por salvar a imagem no disco. Devemos passar para o método a imagem a ser salva, o formato do arquivo e o nome. O Java se encarregará de fazer a conversão do formato da BufferedImage para o formato de saída, podendo haver perdas nesse processo. Uma imagem com transparência, por exemplo, poderá ficar com fundo branco se for gravada num formato que não suporte transparência. Da mesma forma, imagens do tipo JPG geralmente são geradas com uma taxa de compactação alta e podem apresentar distorções. Gravar imagens no disco pode ser uma técnica interessante para criar caches, e evitar penalizar o jogador com operações caras como redimensionamento e desenho toda vez que o jogo é carregado. A assinatura do método é a seguinte: A classe BufferedImage implementa a interface RenderedImage. O boolean retornado pelo método indica se um writer para aquele formato foi encontrado ou não. Para os formatos descritos abaixo, o retorno true é garantido. Outros formatos podem ser instalados através da APIs externas. Formatos de imagem suportados O Java suporta uma série de formatos de imagem. Abaixo, uma breve descrição dos principais: Bitmaps (BMP): É o formato, de longe, mais rápido de ser carregado e pintado. Porém, não suporta transparência e por não conter compactação, gera arquivos de imagem de tamanho grande. JPG: Possui compactação do tipo lossy, ou seja, pode haver perda de qualidade se a compactação for muito agressiva. Por outro lado, mesmo numa taxa baixa de compactação já apresenta um tamanho de arquivo consideravelmente menor que o bitmap. Também não suporta transparência. GIF: Suporta transparência e vários tipos de compactação. Também suporta que várias imagenssejam gravadas no mesmo arquivo, e sejam exibidas na forma de animação em browsers. PNG: Equivalente ao GIF, mas a compactação geralmente é sem perdas. Como é um formato livre, foi extremamente otimizado na API do Java, e é o formato com transparência recomendado pela Sun. Concluindo Nesse artigo, vimos de maneira bem prática e direta como carregar e manipular imagens em Java. Só com o que foi visto, já é possível desenhar qualquer cena do jogo. Sugiro que você tente fazer um programa que carregue algumas imagens, para exercitar o que foi visto. 11 12 //Liberamos o contexto. g2d.dispose(); 1 2 3 static boolean ImageIO.write(RenderedImage im, String formatName, File output) throws IOException ?
Compartilhar