Search code examples
javagraphicsgraphics2d

Java Graphics2D rotate method causes all things painted afterwards to jitter


I have experienced a bit of a problem with javas rotate method. In the following example, I am first calling the rotate method, drawing the rectangle to be rotated and then calling the rotate method again in the exact opposite direction, therefore painting the following rectangles with a 0 degree rotation. However there seems to be a problem with all things painted afterwards, as the second rectangle experiences something I can only describe as a jitter in the y-axis. This does of course only affect things painted after the rotate method is called. Help would be greatly appreciated, as it really is annoying to look at.

package pack;

import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Test extends JFrame{

    private static final long serialVersionUID = 1L;
    public int degrees = 0;
    public Timer timer;

    public Test() {
        setVisible(true);
        setSize(400, 400);
        setContentPane(new paintLabel());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        timer= new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {degrees++;repaint();}
        });
        timer.start();
    }

    public class paintLabel extends JLabel {
        private static final long serialVersionUID = 1L;

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;

            g2d.rotate(Math.toRadians(degrees), 100, 185);
            g2d.fillRect(40, 125, 120, 120);
            g2d.rotate(-Math.toRadians(degrees), 100, 185);
            g2d.fillRect(225, 125, 120, 120);
        }
    }

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

Solution

  • This has nothing to do with precision since you are not rotating an image. You are rotating the Graphics context. The reason the other image didn't rotate is because you simply returned the context to its previous state. You need to then rotate about the center of the second image.

    First you were were doing some things incorrectly.

    • Don't extend JFrame (you weren't overriding anything so just use an instance);
    • Do extend JPanel and put that in the Frame
    • Add the label to the panel
    • Override getPreferredSize to size the JLabel

    Here try this. If you turn on anti-aliasing the jitter goes away. It also tends to smooth out any rough edges. The other image is rotated in the opposite direction. Note that this could just be done inside of paintComponent without the JLabel class.

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.image.renderable.RenderableImage;
    
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Test extends JPanel {
        JFrame f = new JFrame();
        private static final long serialVersionUID = 1L;
        public int degrees = 0;
        public Timer timer;
    
        public Test() {
            f.add(this);
            add(new PaintLabel());
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            timer= new Timer(50,(ae)->{degrees++; repaint();});
            
            timer.start();
            f.pack();
            f.setLocationRelativeTo(null); //center on screen
            f.setVisible(true);
        }
        
    
        public class PaintLabel extends JLabel {
            private static final long serialVersionUID = 1L;
            
            
            public Dimension getPreferredSize() {
                return new Dimension(400,400);
            }
            
            @Override
          public void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setColor(Color.BLUE);
                g2d.rotate(Math.toRadians(degrees), 100, 185);
                g2d.fillRect(40, 125, 120, 120);
                g2d.rotate(-Math.toRadians(degrees), 100, 185);
                g2d.fillRect(225, 125, 120, 120);
            }
        }
    
        public static void main(String[] args) {
            new Test();}
    }