Rotacionando um ImageIcon

28 respostas
G

Pesquisei por tudo quanto era canto e não achei nada.

ai pegando dum canto ou outro depois de passar trabalho consegui rotacionar o ImageIcon:

Por isso vou postar aqui como é:

@Override
	public synchronized void paintIcon(Component c, Graphics g, int x, int y) {

		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.translate(this.getWidth() / 2, this.getHeight() / 2);
		g2.rotate(angle);
		g2.translate(-this.getWidth() / 2, -this.getHeight() / 2);

		super.paintIcon(c, g, x, y);
	}

Agora queria saber se alguém conseguiria me responder , como atualizar esse ImageIcon cada vez que eu mudar a variavel angle!?

28 Respostas

V

Crie um método setAngle assim:

public void setAngle(float angle) { this.angle = angle; invalidate(); }

G

Ja criei um setAngle , mas preciso repintar a imagem depois de setar o angulo.

é isso que nao consigo. D=

V

Seu setAngle chamava invalidate?

G

não.

V

Então chame.

G

mas nao existe esse metodo.

V

Sorry, então chame invalidate() no JLabel que contém seu ImageIcon.

G

nada D:

ja tentei cria um JFrame novo sem nada na tela e tal , mas ele nao atualiza a imagem de jeito nenhum , o metodo pra redimensionar imagem tambm nao funciona.

ta muito estranho isso.

G

tentei usar setImage(getImage()); pois ele seria obrigado a atualizar , mas nao acontesse nada;

G

Vou postar meu código:

package br.com.view;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.net.URL;

import javax.swing.ImageIcon;

public class JImage extends ImageIcon {
	
	private double angle = Math.toRadians(0);

	public JImage() {
	}

	public JImage(String arg0) {
		super(arg0);
	}

	public JImage(URL arg0) {
		super(arg0);
	}

	public JImage(Image arg0) {
		super(arg0);
	}

	public JImage(byte[] arg0) {
		super(arg0);
	}

	public JImage(String arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(URL arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(Image arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(byte[] arg0, String arg1) {
		super(arg0, arg1);
	}

	public int getHeight() {
		return this.getIconHeight();
	}

	public int getWidth() {
		return this.getIconWidth();
	}

	public void setHeight(int height) {
		this.setImage(this.getImage().getScaledInstance(getWidth(), height,
				Image.SCALE_AREA_AVERAGING));
	}

	public void setWidth(int width) {
		this.setImage(this.getImage().getScaledInstance(width, getHeight(),
				Image.SCALE_AREA_AVERAGING));
	}

	public void setSize(int width, int height) {
		this.setImage(this.getImage().getScaledInstance(width, height,
				Image.SCALE_AREA_AVERAGING));
	}

	public Dimension getSize() {
		return new Dimension(getWidth(), getHeight());
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
	}

	@Override
	public synchronized void paintIcon(Component c, Graphics g, int x, int y) {

		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.translate(this.getWidth() / 2, this.getHeight() / 2);
		g2.rotate(angle);
		g2.translate(-this.getWidth() / 2, -this.getHeight() / 2);

		super.paintIcon(c, g, x, y);
	}

}
V

Eu não faria isso com o ImageIcon, e sim montaria meu proprio JComponent personalizado. Aí basta colocar esse código no método paintComponent desse componente.
É mais fácil de lidar, pois o componente pode notificar o sistema de janelas que ele precisa ser atualizado.

Já posto aqui como ficaria.

G

hm…

Ja fiz isso usando um JPanel e funcionou perfeitamente =D

é que estou criando uma espécie de biblioteca gráfica , e estava tentando refazer o ImageIcon , porque as classes padrões do java que lidam com imagens são muito precárias. D:

mas vou esperar o resultado sua sua classe ^^

G

o setHeight esta funcionando.

G

o problema nao é na classe da imagem , e sim na classe que pinta a imagem vou tentar achar o erro aqui.

Valeu!

G

Tudo feito a classe ta certa , foi uma estupidez minha que eu fiz na classe.

JImage i = new JImage("Squirtle.png"); i.setAngle(-angle); this.setIcon(new JImage("Squirtle.png"));

eu tinha feito isso. kkkkkkkk’

Então ta resolvido , mas acho que essa classe é uma solução pro pessoal , porque sofri pra “não” acha uma solução e tive que faze só testando classe por classe.

Vlw!

G

Arrume na classe para nao dar problema :

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.net.URL;

import javax.swing.ImageIcon;

public class JImage extends ImageIcon {
	
	private double angle = Math.toRadians(0);

	public JImage() {
	}

	public JImage(String arg0) {
		super(arg0);
	}

	public JImage(URL arg0) {
		super(arg0);
	}

	public JImage(Image arg0) {
		super(arg0);
	}

	public JImage(byte[] arg0) {
		super(arg0);
	}

	public JImage(String arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(URL arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(Image arg0, String arg1) {
		super(arg0, arg1);
	}

	public JImage(byte[] arg0, String arg1) {
		super(arg0, arg1);
	}

	public int getHeight() {
		return this.getIconHeight();
	}

	public int getWidth() {
		return this.getIconWidth();
	}

	public void setHeight(int height) {
		this.setImage(this.getImage().getScaledInstance(getWidth(), height,
				Image.SCALE_AREA_AVERAGING));
	}

	public void setWidth(int width) {
		this.setImage(this.getImage().getScaledInstance(width, getHeight(),
				Image.SCALE_AREA_AVERAGING));
	}

	public void setSize(int width, int height) {
		this.setImage(this.getImage().getScaledInstance(width, height,
				Image.SCALE_AREA_AVERAGING));
	}

	public Dimension getSize() {
		return new Dimension(getWidth(), getHeight());
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
		this.setImage(getImage());
	}

	@Override
	public synchronized void paintIcon(Component c, Graphics g, int x, int y) {

		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.translate(this.getWidth() / 2, this.getHeight() / 2);
		g2.rotate(angle);
		g2.translate(-this.getWidth() / 2, -this.getHeight() / 2);

		super.paintIcon(c, g, x, y);
	}

}
V
Segue um exemplo completo:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class JImage extends JComponent {
	private BufferedImage image;
	private BufferedImage drawImage;
	private double angle = 0;

	public JImage() {
		image = drawImage = newOptimizedImage(1, 1, false, BufferedImage.OPAQUE); 		
	}

	public void setImage(File file) throws IOException {
		setImage(ImageIO.read(file));
		repaint();
	}

	public void setImage(URL resource) throws IOException {
		setImage(ImageIO.read(resource));
		repaint();
	}

	public void setImage(BufferedImage image) {
		this.image = image;
		this.drawImage = image;
		repaint();
	}

	private static BufferedImage newOptimizedImage(int width, int height,
			boolean alpha, int transparency) {
		GraphicsConfiguration destination = GraphicsEnvironment
				.getLocalGraphicsEnvironment().getDefaultScreenDevice()
				.getDefaultConfiguration();
		return alpha ? destination.createCompatibleImage(width, height,
				transparency) : destination
				.createCompatibleImage(width, height);
	}

	public void setHeight(int height) {
		changeSize(height, image.getHeight());
	}

	public void setWidth(int width) {
		changeSize(width, image.getHeight());
	}
	
	public void changeSize(int width, int height) {
		drawImage = newOptimizedImage(width, height, image
				.getColorModel().hasAlpha(), image.getTransparency());
		
		Graphics2D g2d = drawImage.createGraphics();
		g2d.drawImage(image, 0, 0, drawImage.getWidth(), drawImage.getHeight(), 
				0, 0, image.getWidth(), image.getHeight(), 
				null);		
		g2d.dispose();		
		repaint();
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
		double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
		int w = image.getWidth(), h = image.getHeight();
		int neww = (int) Math.floor(w * cos + h * sin);
		int newh = (int) Math.floor(h * cos + w * sin);
		drawImage = newOptimizedImage(neww, newh, 
				image.getColorModel().hasAlpha(), image.getTransparency());
		Graphics2D g = drawImage.createGraphics();
		g.translate((neww - w) / 2, (newh - h) / 2);
		g.rotate(angle, w / 2, h / 2);
		g.drawRenderedImage(image, null);
		g.dispose();		
		repaint();
	}

	@Override
	public Dimension getPreferredSize() {
		return new Dimension(drawImage.getWidth(), drawImage.getHeight());
	}

	@Override
	protected void paintComponent(Graphics g) {		
		Graphics2D g2d = (Graphics2D) g.create();
		g2d.setColor(Color.BLACK);
		g2d.fillRect(0, 0, getWidth(), getHeight());
		g2d.drawImage(drawImage, 0, 0, null);
		g2d.dispose();
	}
	
	public static void main(String[] args) throws IOException {
		final JFrame frame = new JFrame();
		
		final JImage image = new JImage();
		image.setImage(JImage.class.getResource("fish.jpg"));
		frame.add(BorderLayout.CENTER, new JScrollPane(image));
		
		JPanel pnlTop = new JPanel(new FlowLayout());
		pnlTop.add(new JLabel("Angulo:"));		
		final JTextField txtAngle = new JTextField(5);
		pnlTop.add(txtAngle);
		
		JButton btnChange = new JButton("Alterar ângulo");
		btnChange.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				try 
				{
					double angle = Double.parseDouble(txtAngle.getText());
					image.setAngle(Math.toRadians(angle));
				}
				catch (Exception exception)
				{
					JOptionPane.showMessageDialog(frame, "Ângulo inválido!");
				}
			}
		});
		pnlTop.add(btnChange);
		
		frame.add(BorderLayout.NORTH, pnlTop);
		frame.setSize(800,600);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);
		frame.setTitle("Teste de imagem");
		frame.setVisible(true);
	}
}

G

Hm…

Interessante isso.

O único problema é que eu estou refazendo a classe image , mas é uma boa isso , vou dar uma estuda no código , vai ser útil. ^^

V

Segue o .jar, se quiser ver rodando.

Note que a classe JImage não precisaria ter aquele main ali. Só deixei ele de exemplo.

G

Hm… ficou muito bom. =D

Mas minha classe esta funcionado perfeitamente tambm , foi só aquele erro idiota. xD

Mas nunca tinha criado um Componente , vai ajudar bastante aqui. ^^

Vlw mesmo kra!

E

Ok, mas e se eu quiser fazer isso com um texto, como eu faria?

V

É um pouco mais complicado.

  1. Para descobrir as dimensões da imagem, você deve usar o método getStringBounds do FontMetrics retornado pelo método getFontMetrics() classe Graphics2D.
    Sobre esse bounds, faça o cálculo das linhas 90 e 91, e os calculos de transformação das linhas 95 e 96.

  2. Troque o método de drawImage para drawString.

V
Eis o exemplo:
package br.com.guj;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.io.IOException;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class JTextImage extends JComponent {
	private double angle = 0;
	private String text;
	private int x;
	private int y;

	public JTextImage(int x, int y, String text, String fontName, int style, int size) {
		this.text = text;
		this.x = x;
		this.y = y;
		setFont(new Font(fontName, style, size));
	}
		
	public String getText() {
		return text;
	}
	
	public final void setText(String text) {		
		this.text = text;
		repaint();
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
		repaint();
	}

	private Rectangle2D calculateTextBounds(Graphics g)
	{
		return getGraphics().getFontMetrics().getStringBounds(text, getGraphics());		
	}
	private Dimension calculateRotatedDimension(Graphics g, Rectangle2D bounds)
	{
		double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
				
		double w = bounds.getWidth(), h = bounds.getHeight();
		int neww = (int) Math.floor(w * cos + h * sin);
		int newh = (int) Math.floor(h * cos + w * sin);
		
		return new Dimension(neww, newh);
	}
	@Override
	public Dimension getPreferredSize() {
		Graphics g = getGraphics();
		return calculateRotatedDimension(g, calculateTextBounds(g));
	}

	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g.create();
		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		//Limpa o fundo
		g2d.setColor(Color.WHITE);
		g2d.fillRect(0, 0, getWidth(), getHeight());

		//Calcula o tamanho do texto rotacionado
		Rectangle2D oldSize = getGraphics().getFontMetrics().getStringBounds(text, getGraphics());
		Dimension newSize = calculateRotatedDimension(g2d, oldSize);
		//Faz a translação do sistema coordenado até o ponto onde se deseja desenhar
		g2d.translate(x, y);
		
		//Faz a translação do sistema até o centro do texto
		g2d.translate(
				(newSize.getWidth() - oldSize.getWidth()) / 2, 
				(newSize.getHeight() - oldSize.getHeight()) / 2);		
		//Rotaciona o sistema coordenado
		g2d.rotate(angle, oldSize.getWidth() / 2, oldSize.getHeight() / 2);
		
		//Escreve o texto no novo sistema
		g2d.setColor(Color.BLUE);
		g2d.drawString(text, 0, 0);
		
		g2d.dispose();
	}

	public static void main(String[] args) throws IOException {
		final JFrame frame = new JFrame();

		final JTextImage image = new JTextImage(100, 100, "Olá GUJ!", "Verdana", Font.BOLD, 32);
		frame.add(BorderLayout.CENTER, new JScrollPane(image));

		JPanel pnlTop = new JPanel(new FlowLayout());
		pnlTop.add(new JLabel("Angulo:"));
		final JTextField txtAngle = new JTextField(5);
		pnlTop.add(txtAngle);

		JButton btnChange = new JButton("Alterar ângulo");
		btnChange.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					double angle = Double.parseDouble(txtAngle.getText());
					image.setAngle(Math.toRadians(angle));
				} catch (Exception exception) {
					JOptionPane.showMessageDialog(frame, "Ângulo inválido!");
				}
			}
		});
		pnlTop.add(btnChange);

		frame.add(BorderLayout.NORTH, pnlTop);
		frame.setSize(800, 600);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);
		frame.setTitle("Teste de imagem");
		frame.setVisible(true);
	}
}
G

Para facilitar , acho que poderia colocar aquele meu codigo no metodo paint de um JLabel.

public class Novo_JLabel extends JLabel{
	
	private double angle = Math.toRadians(0);
	
	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
		this.repaint();
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.translate(this.getWidth() / 2, this.getHeight() / 2);
		g2.rotate(angle);
		g2.translate(-this.getWidth() / 2, -this.getHeight() / 2);
		super.paintComponent(g);
	}
	
}

Isso funciona ViniGodoy?

V

Esse seu código tem uma série de problemas:
a) Ele modifica o contexto gráfico. Você não pode alterar o estado do Graphics2D e deixa-lo alterado quando sair do método paintComponent. É para isso que o create() serve. Isso poderia ser facilmente alterado usando
Graphics2D g2d = (Graphics2D) g.create();
E colocando g2d.dispose() após a linha do super.

b) Ele não fornece o novo tamanho ao getPreferredSize(). Lembre-se que ao rotacionar o texto, ele muda de tamanho. Um texto rotacionado em 90º será mais alto do que largo e o getWidth() e o getHeight() continará retornando o tamanho antigo.

G

o b eu intendi de boa , não implementei tudo , foi só o básico.

Mas no a eu não intendi nada. D:

V

O objeto Graphics que você recebe no parâmetro não é usado só no seu componente. Ele pode ser usado por outras coisas no Swing, como a pintura da borda do componente, ou dos componentes que estão sobre ele.
Por isso, você não deve alterar coisas nesse objeto. Por exemplo, se você fizer um:
g.setColor(Color.RED);

Deve depos restaurar a cor que estava lá antigamente. Por isso o certo seria fazer:

//Grava a cor antiga. Color oldColor = g.getColor(); g.setColor(Color.RED); //Desenha aqui com cor vermelha g.setColor(oldColor);

O pessoal da Sun percebeu que era muito trabalhoso fazer isso para todas as propriedades do Graphics. Por isso, criaram um meio mais prático, de você copiar todo o objeto Graphics. Assim você trabalha na cópia, modifica-a como quiser, e no final descarta essa cópia. Note que o Graphics é apenas a caneta, não o desenho. Por isso, você só estaria descartando o objeto que faz a pintura, não a pintura em si. A mesma situação fica assim:

Graphics2D g2d = g.create(); //Criamos a cópia g2d.setColor(Color.RED); //Definimos vermelho //pintamos com o g2d g2d.dispose(); //Descartamos a cópia

E assim o g não estaria modificado, podendo ser usado para qualquer outro tipo de pintura que venha na sequência do método.

V

No caso, do a), falei que dava para corrigir assim:

public class Novo_JLabel extends JLabel{
	
	private double angle = Math.toRadians(0);
	
	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
		this.repaint();
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g.create(); //usamos o create() para criar a cópia.
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.translate(this.getWidth() / 2, this.getHeight() / 2);
		g2.rotate(angle);
		g2.translate(-this.getWidth() / 2, -this.getHeight() / 2);
		super.paintComponent(g2); //Chamamos o método de cima com a cópia tb.
                g2.dispose(); //descartamos a cópia
	}	
}
Criado 3 de setembro de 2011
Ultima resposta 8 de set. de 2011
Respostas 28
Participantes 3