How to Rotate an Image Gradually in Swing

Rotate BufferedImage Inside JPanel

I solved my own issue. The problem lay in the code:

myPicture.getType()

Since there is a lot of variability in the types of images you could put in to the program, the results are going to be unpredictable when you start drawing into the new BufferedImage. I solved the problem by setting the type explicitly, which in my case required

BufferedImage.TYPE_INT_ARGB

so the full statement read:

BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

Rotate 90 degree to right image in java

As discussed here, you can use AffineTransformOp to rotate an image by Math.PI / 2; this is equivalent to rotating the image clockwise 90°, as shown here. See also Handling 90-Degree Rotations.

How to rotate a non-square image in Java?

When you rotate an image the width and height also change and your code doesn't take this into account.

Here is some old code I have lying around that should work better:

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation
{
BufferedImage image;
JLabel label;

public Rotation(BufferedImage image)
{
this.image = image;
}

private BufferedImage getImage(double theta)
{
// Determine the size of the rotated image

double cos = Math.abs(Math.cos(theta));
double sin = Math.abs(Math.sin(theta));
double width = image.getWidth();
double height = image.getHeight();
int w = (int)(width * cos + height * sin);
int h = (int)(width * sin + height * cos);

// Rotate and paint the original image onto a BufferedImage

BufferedImage out = new BufferedImage(w, h, image.getType());
Graphics2D g2 = out.createGraphics();
g2.setPaint(UIManager.getColor("Panel.background"));
g2.fillRect(0,0,w,h);
double x = w/2;
double y = h/2;
AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
x = (w - width)/2;
y = (h - height)/2;
at.translate(x, y);
g2.drawRenderedImage(image, at);
g2.dispose();
return out;
}

private JLabel getLabel()
{
ImageIcon icon = new ImageIcon(image);
label = new JLabel(icon);
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}

private JSlider getSlider()
{
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
BufferedImage bi = getImage(Math.toRadians(value));
label.setIcon(new ImageIcon(bi));
}
});
return slider;
}

public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
String path = "mong.jpg";
ClassLoader cl = Rotation.class.getClassLoader();
BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
Rotation r = new Rotation(bi);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(r.getLabel()));
f.getContentPane().add(r.getSlider(), "South");
f.pack();
f.setLocation(200,200);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}

Edit:

Another option is to create an Icon, then you can use the Rotated Icon. Then you can rotate and paint the icon in your painting code. Something like:

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation3 extends JPanel
{
private Icon icon;
private Icon rotated;
private int degrees;

public Rotation3(BufferedImage image)
{
icon = new ImageIcon( image );
setDegrees( 0 );
setPreferredSize( new Dimension(600, 600) );
}

@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);

double radians = Math.toRadians( degrees );
rotated = new RotatedIcon(icon, degrees);

// translate x/y so Icon is rotated around a specific point (300, 300)

int x = 300 - (rotated.getIconWidth() / 2);
int y = 300 - (rotated.getIconHeight() / 2);
rotated.paintIcon(this, g, x, y);

g.setColor(Color.RED);
g.fillOval(295, 295, 10, 10);
}

public void setDegrees(int degrees)
{
this.degrees = degrees;
double radians = Math.toRadians( degrees );
rotated = new RotatedIcon(icon, degrees);
repaint();
}

public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
String path = "dukewavered.gif";
ClassLoader cl = Rotation3.class.getClassLoader();
BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
final Rotation3 r = new Rotation3(bi);

final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});

JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}

Edit 2:

Even easier than I thought here is an example:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation2 extends JPanel
{
BufferedImage image;
int degrees;
int point = 250;

public Rotation2(BufferedImage image)
{
this.image = image;
setDegrees( 0 );
setPreferredSize( new Dimension(600, 600) );
}

@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g.create();

double radians = Math.toRadians( degrees );
g2.translate(point, point);
g2.rotate(radians);
g2.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2.drawImage(image, 0, 0, null);

g2.dispose();

g.setColor(Color.RED);
g.fillOval(point - 5, point - 5, 10, 10);
}

public void setDegrees(int degrees)
{
this.degrees = degrees;
repaint();
}

public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
// String path = "mong.jpg";
String path = "dukewavered.gif";
ClassLoader cl = Rotation2.class.getClassLoader();
BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
final Rotation2 r = new Rotation2(bi);

final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});

JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}

The rotation code was taken from: How to rotate an image gradually in Swing?

Rotating Image (getting it back to original position)

I looked that over, but the code rotates the entire panel; I just want to rotate one Image on a fixed rotate point.

Two things may help guide your understanding:

  • The example cited uses rotate(double theta); it is preceeded by a translation to the origin and succeeded by a translation the panel's center. Note that the operations are performed in the apparent reverse of the declared order. Your example (may have meant to) invoke rotate(double theta, double anchorx, double anchory). The two effect the same result, the latter being a convenient alternative to the former.

  • This example contrasts how one can transform the graphics context (g2d) or the image itself. Your example invokes drawImage(Image img, AffineTransform xform, ImageObserver obs), which concatenates xform to the existing graphics transform; this affects all subsequent drawing. It may be easier to keep them separate.

Java: Rotating Images

This is how you can do it. This code assumes the existance of a buffered image called 'image' (like your comment says)

// The required drawing location
int drawLocationX = 300;
int drawLocationY = 300;

// Rotation information

double rotationRequired = Math.toRadians (45);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

// Drawing the rotated image at the required drawing locations
g2d.drawImage(op.filter(image, null), drawLocationX, drawLocationY, null);

Rotate graphics2D

The graphics2D.rotate call transforms subsequent renderings, so you need to repaint and put the rotate call just before you render the text.

See also: Javadocs

Also, the method requires the input to be in radians.

Java: Rotate image towards mouse position?

You should not include the image in a JLabel. It simply does not offer much flexibility. The positioning may be odd, and you can't draw the image rotated. (You could only create a rotated image and put this into the label, but this would be inefficient, and the sizing and position issues would drive you mad).

Instead, you should paint your image manually, in the overridden paintComponent method of a JPanel. There are still some details to consider. For example, the image should probably have a certain position, and the position should probably refer to the center of the image - namely, to the point that it will be rotated about.

However, when you have computed the angle of the line between the image center and the mouse position, you can simply paint the image with the desired orientation, by

  • moving the image so that its center is at the origin
  • rotating the image
  • moving the image so that its center is at the desired position

Here is a MCVE:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ImageFollowingMouseTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}

private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ImageFollowingMousePanel());
f.setSize(400,400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}

}

class ImageFollowingMousePanel extends JPanel implements MouseMotionListener
{
private final BufferedImage image;
private Point imagePosition = new Point(150,150);
private double imageAngleRad = 0;

public ImageFollowingMousePanel()
{
BufferedImage i = null;
try
{
i = ImageIO.read(new File("6Wu0b.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
image = i;
addMouseMotionListener(this);
}

protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

int cx = image.getWidth() / 2;
int cy = image.getHeight() / 2;
AffineTransform oldAT = g.getTransform();
g.translate(cx+imagePosition.x, cy+imagePosition.y);
g.rotate(imageAngleRad);
g.translate(-cx, -cy);
g.drawImage(image, 0, 0, null);
g.setTransform(oldAT);

}

@Override
public void mouseDragged(MouseEvent e)
{
}

@Override
public void mouseMoved(MouseEvent e)
{
double dx = e.getX() - imagePosition.getX();
double dy = e.getY() - imagePosition.getY();
imageAngleRad = Math.atan2(dy, dx);
repaint();
}

}

Java Swing PaintComponent rotating jlabels

There are a few things you can do

  1. Create a copy of the Graphics context, use something like Graphics2D g2d = (Graphics2D)g.create();. Once you have finished painting, call Graphics#dispose on the copy to free any resources it might have allocated. This basically allows you to change the properties of the copy, without effecting the original, but still paint to the same buffer.
  2. Obtain a copy of the original AffineTransform from the Graphics2D, then you can apply your own AffineTransform and reset it when you are finished, see Graphics2D#get/setTransform

For example...

Spinny

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestRotate01 {

public static void main(String[] args) {
new TestRotate01();
}

public TestRotate01() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private CrossShape prop;

private double angle;

public TestPane() {
prop = new CrossShape(50, 50);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
angle += 5;
repaint();
}
});
timer.start();
}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform current = g2d.getTransform();

int x = 25;
int y = (getHeight() - prop.getBounds().height) / 2;

AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(angle), prop.getBounds().width / 2, prop.getBounds().height / 2);
g2d.setTransform(at);
g2d.setColor(Color.RED);
g2d.draw(prop);

// Reset...
// Equally, you could dispose of the g2d and create a new copy
g2d.setTransform(current);

x = getWidth() - 25 - prop.getBounds().width;
y = (getHeight() - prop.getBounds().height) / 2;

at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(-angle), prop.getBounds().width / 2, prop.getBounds().height / 2);
g2d.setTransform(at);
g2d.setColor(Color.BLUE);
g2d.draw(prop);

g2d.dispose();
}

}

public class CrossShape extends Path2D.Double {

public CrossShape(int width, int height) {

moveTo(0, 0);
lineTo(width, height);
moveTo(width, 0);
lineTo(0, height);

}

}

}


Related Topics



Leave a reply



Submit