Animação usando BufferedImage

7 respostas
E

Olá pessoal…
É o seguinte…Tenho uma classe que extende um JPanel que é construída a partir de um contunto de Objetos Ponto (quan guardam uma cor e um par de coordenadas cartezianas). Essa classe implementa Runnable.

Minha idéia é ter uma BufferedImage e, nela, ir plotando os pontos aleatoriamente. A cada novo ponto plotado nela eu queria redesenhá-la no meu JPanel…

A seguir, segue o código da classe:

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.ArrayList;
import java.util.Collections;
import java.awt.image.BufferedImage;

public class PainelAnima extends JPanel implements Runnable
{
	private IO io;
	private ArrayList<Ponto> pontosOriginais;
	private ArrayList<Ponto> pontosAPlotar;
	private Thread anima;
	private int largura;
	private int altura;
	private int diametroDoPincel;
	private BufferedImage imagem;
	
	public PainelAnima(ArrayList pontosOriginais,int largura, int altura, int diametroDoPincel)throws Exception
	{
		this.pontosOriginais = pontosOriginais;
		this.pontosAPlotar = new ArrayList();
		this.imagem = new BufferedImage(largura, altura, BufferedImage.TYPE_INT_BGR );
		
		this.largura = largura;
		this.altura = altura;
		
		this.diametroDoPincel = diametroDoPincel;
		
		(imagem.getGraphics()).setColor(new Color(0,0,0));
        (imagem.getGraphics()).fillRect(0,0,largura+10,altura+10);
		
		anima = new Thread(this,"anima");
		anima.start();	
	}
	
	public void run()
	{
		while(true)
		{
			if(pontosAPlotar.isEmpty())
			{
				for(int i=0;i<pontosOriginais.size();i++)
				{
					pontosAPlotar.add((pontosOriginais.get(i)).clonar());
				}
				Collections.shuffle(pontosAPlotar);
				
			}
			else
			{
				plotarPonto(pontosAPlotar.get(0));
				pontosAPlotar.remove(0);
				this.repaint();
			}
			
			try
			{
				Thread.sleep(10);
			}
			catch(Exception ex)
			{
			}
		}
	}
	
	private void plotarPonto(Ponto ponto)
	{
		(imagem.getGraphics()).setColor(ponto.getCor());
		(imagem.getGraphics()).fillOval(ponto.getX(),ponto.getY(),diametroDoPincel,diametroDoPincel);
		
		//System.out.println("plotando ponto ["+ponto.getX()+","+ponto.getY()+"]");
	}
	
	public void paintComponent(Graphics g) 
    {
        super.paintComponent(g);
        
        g.drawImage(imagem,0,0,null);
    }
}

Todavia, eu starto a aplicação, abro uma instância desa minha classe dentro de um JFrame e a única coisa que aparece é o fundo branco, mais nada…
Alguém saberia me dizer onde foi que eu errei?

Desde já agradeço…

7 Respostas

ViniGodoy

Algumas observações:

1. Você cria em seu construtor uma thread, que já trabalha sobre o JPanel. O problema é que ali o JPanel ainda nem foi construído direito;
2. Você não usa a largura e a altura do JPanel. Tem certeza que esses parâmetros estão sendo fornecidos corretamente? O ideal seria já usar as propriedades do próprio JPanel.
3. Como você está fazendo para inserir o seu JPanel no seu JFrame? Se você está usando o layout padrão (flowlayotu) seu panel será redimensionado para o tamanho 0.
4. Você não está usando corretamente o objeto graphics do BufferedImage;
5. Não entendi muito o propósito do seu programa. Ele vai plotar os 3 pontos em ordem aleatória. Isso é extremamente rápido. Depois, vai exibir os 3 pontos no local desejado. Então, para que embaralhar sempre?

Em todo caso, preparei o código abaixo com algumas correções:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JPanel;

public class PainelAnima extends JPanel implements Runnable {
    private List&lt;Ponto&gt; pontosOriginais;
    private List&lt;Ponto&gt; pontosAPlotar;
    private Thread anima;
    private int diametroDoPincel;
    private BufferedImage imagem;

    private BufferedImage getImagem() {
        if (imagem == null)
            imagem = new BufferedImage(getWidth(), getHeight(),
                    BufferedImage.TYPE_INT_BGR);
        return imagem;
    }

    public PainelAnima(List&lt;Ponto&gt; pontosOriginais, int diametroDoPincel) {
        super();
        this.setOpaque(true);
        this.pontosOriginais = pontosOriginais;
        this.pontosAPlotar = new ArrayList&lt;Ponto&gt;();
        this.diametroDoPincel = diametroDoPincel;
        anima = new Thread(PainelAnima.this, &quot;anima&quot;);
        anima.start();
    }

    public void run() {
        // Como isso foi chamado no construtor, damos um tempinho até pintar a
        // imagem. Senão o getWidth() e getHeight() não existirão.
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        
        Graphics2D g2d = getImagem().createGraphics();
        g2d.setColor(new Color(0, 0, 0));
        g2d.fillRect(0, 0, getImagem().getWidth(), getImagem().getHeight());
        g2d.dispose();

        while (true) {
            if (pontosAPlotar.isEmpty()) {
                for (int i = 0; i &lt; pontosOriginais.size(); i++) {
                    pontosAPlotar.add((pontosOriginais.get(i)).clonar());
                }
                Collections.shuffle(pontosAPlotar);

            } else {
                plotarPonto(pontosAPlotar.get(0));
                pontosAPlotar.remove(0);
                this.revalidate();
                this.repaint();
            }

            try {
                Thread.sleep(10);
            } catch (Exception ex) {
            }
        }
    }

    private void plotarPonto(Ponto ponto) {
        Graphics g2d = imagem.createGraphics();
        g2d.setColor(ponto.getCor());
        g2d.fillOval(ponto.getX(), ponto.getY(), diametroDoPincel,
                diametroDoPincel);
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(imagem, 0, 0, null);
    }
}
public class Frame extends JFrame {
    public PainelAnima painel;
    public Frame()
    {
        setLayout(new BorderLayout());
        painel = new PainelAnima(Arrays.asList(new Ponto(10,10, Color.BLUE),
                new Ponto(15,15, Color.RED),
                new Ponto(20,20, Color.WHITE)), 5);
        add(painel, BorderLayout.CENTER);
        setBounds(10,10,300,300);
    }
    
    public static void main(String[] args) {
        new Frame().setVisible(true);
    }
}

Essa ainda está longe de ser a melhor maneira de criar animações em Java. Eu recomendo fortemente, já que você se interessa pelo assunto, a leitura desses materiais:
http://www.cokeandcode.com/node/6
http://fivedots.coe.psu.ac.th/~ad/jg/ch1/index.html
http://java.sun.com/products/jfc/tsc/articles/painting/index.html#swing
http://java.sun.com/j2se/1.5.0/docs/guide/2d/spec/j2d-bookTOC.html

E

Opa…

1-É verdade, não tinha percebido o problema…
2-Na verdade eu uso a largura e a altura que são passados no construtor, que não se referem às dimensões do JPanel, mas às dimensões da imagem a ser plotada…
3-De fato, deixei o layout padrão. Sempre usou o BorderLayout.CENTER, mas como isso era só teste de um conceito, acabei esquecendo de prestar atenção em algumas coisas…
4-Certo. Você poderia me dizer exatamente o que eu estou fazendo de errado? Me ajudaria muito para futuras empreitadas…
5-O propósito é gerar um efeito…hã…artístico com um imagem, causando uma certa impressão (que estou buscando) sobre uma imagem. Ou seja, eu tenho bem mais que 3 pontos. Dependendo da imagem, eu tenho 3 megapontos, em algumas 7 MPontos…E assim vai…

Vou dar uma olhada com calma neste código…

Valeu pela ajuda…

ViniGodoy

A forma correta de usar o BufferedImage está no meu exemplo. É através do método createGraphics(). O resto, é igual ao que você já faz. Só que fica um dispose() a mais, depois de terminar de usar o Graphics.

A questão da altura e largura foi só uma dúvida mesmo, e uma sugestão. Como vc estava fazendo aquelas construções no constutor, imaginei que seu problema era o fato de ali, getWidth() e getHeigth() do painel ainda retornarem 0. Mas já que a imagem e o painel diferem no tamanho, o jeito que vc fez está certo.

Legal, você quer dar o efeito da imagem “aparecer aos poucos”? Acho que isso é chamado de materialize. Se eu não me engano, é um dos efeitos descritos aqui:
http://fivedots.coe.psu.ac.th/~ad/jg/ch04/index.html

Você poderia ler esse capítulo todo. Há muita coisa interessante sobre manipulação de imagens aí.

E

Valeu pela força, cara…
Postei a dúvida aqui no GUJ já imaginando que você teria essa resposta…Sem dúvidas você é a pessoa mais familiarizadas com a API gráfica do JAVA que eu conheço e a que mais dá força pra galera em apuros nessa área…

Valeu mesmo :slight_smile:

E

É…Analisando o seu código, percebi que eu estava equivocado em alguns conceitos…
Fiz mais algumas modificações na coisa toda, porque meu intuito, com esta aplicação, é estudar um pouco mais da API gráfica do Java.

Agora a pouco me deu uma idéia: Eu ploto alguns pontos em minha BufferedImage, como o diâmetro das elipses que representam cada pixel da minha imagem são maiores que um, os pontos têm áreas de sobreposição com pontos anteriormente plotados. Minha idéia é a seguinte, existe como eu fazer com que quando eu ploto um ponto eu faça sobreposição de cor com a cor que está abaixo? Mesclar…Um exemplo é o seguinte, plotei um ponto preto (0,0,0), em seguida, ploto um ponto branco (255,255,255). Vamos supor que esses dois pontos (que são elipses, na verdade) têm uma área em comum. Teria como, nessa área a cor resultante ser uma média de ambas?

Dei uma olhada na seguinte classe:
java.awt.AlphaComposite
Será que ela me ajuda no que estou querendo fazer?

Valeu…

E

Opa…Descobri como fazer o que eu queria…
Era com a AlphaComposite mesmo…

g2d.setComposite(java.awt.AlphaComposite.getInstance(java.awt.AlphaComposite.SRC_OVER, 0.5f));

Agora estou testando qual regra é a mais interessante para o que eu quero…

ViniGodoy

Engraçado, não recebi o seu e-mail com a dúvida do Alpha.
Mas o caminho é esse mesmo… que bom q vc achou por si só. Em todo caso, tem muito material sobre esses recursos nos links ali em cima.

Criado 3 de setembro de 2008
Ultima resposta 4 de set. de 2008
Respostas 7
Participantes 2