How to rotate an image gradually in Swing?

In addition to @tulskiy’s helpful observations, I would add two points:

  1. Always construct your GUI on the event dispatch thread, as shown below.

  2. An sscce should be a Short, Self Contained, Correct (Compilable), Example. As a convenience, don’t require others to recreate multiple public classes; use top-level (package-private) or nested classes. As this is a graphics problem, use a public or synthetic image that reflects your problem.

In the example below, paintComponent() alters the graphics context’s transform to effect the rotation. Note that the operations are performed in the (apparent) reverse of the declaration order: First, the image’s center is translated to the origin; second, the image is rotated; third, the image’s center is translated to the center of the panel. You can see the effect by resizing the panel.

Addendum: See also this alternative approach using AffineTransform.

image

package overflow;

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

/**
 * @see https://stackoverflow.com/questions/3371227
 * @see https://stackoverflow.com/questions/3405799
 */
public class RotateApp {

    private static final int N = 3;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setLayout(new GridLayout(N, N, N, N));
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                for (int i = 0; i < N * N; i++) {
                    frame.add(new RotatePanel());
                }
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}


class RotatePanel extends JPanel implements ActionListener {

    private static final int SIZE = 256;
    private static double DELTA_THETA = Math.PI / 90;
    private final Timer timer = new Timer(25, this);
    private Image image = RotatableImage.getImage(SIZE);
    private double dt = DELTA_THETA;
    private double theta;

    public RotatePanel() {
        this.setBackground(Color.lightGray);
        this.setPreferredSize(new Dimension(
            image.getWidth(null), image.getHeight(null)));
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {
                image = RotatableImage.getImage(SIZE);
                dt = -dt;
            }
        });
        timer.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
        g2d.rotate(theta);
        g2d.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
        g2d.drawImage(image, 0, 0, null);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        theta += dt;
        repaint();
    }

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

}

class RotatableImage {

    private static final Random r = new Random();

    static public Image getImage(int size) {
        BufferedImage bi = new BufferedImage(
            size, size, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
        g2d.setStroke(new BasicStroke(size / 8));
        g2d.drawLine(0, size / 2, size, size / 2);
        g2d.drawLine(size / 2, 0, size / 2, size);
        g2d.dispose();
        return bi;
    }
}

Leave a Comment