Search code examples
javabufferedimagepaintcomponent

Flickering when drawing onto bufferedImage


I have a 3d-object composed of multiple polygons that I draw using graphics2D. When I rotate it it, it seems as if it has not enough time to draw the entire object at every frame since at some frames, some of the polygons are simply missing(not drawn). I don't understand how that can be since I in paintComponent first draw all the polygons onto the bufferedImage myImg, and then draw the finished image onto the screen. When I remove clearRect, this issue is resolved but then of course it doesn't remove the last frame's drawing before drawing the next.

Note: I'm an amateur but I've tried really hard understanding and so this is my last resort and would be really glad to get some help. The code (with unnecessary code removed is as follows) :

 public class Main {

       long temp = System.currentTimeMillis() + frameRate;

       public static void main(String[] args) {
          myGUI = new GUI(width, height);       
          while(true) {
            if (System.currentTimeMillis() >= temp) {
              temp += frameRate;
              rotateObject();
              myGUI.myCanvas.myLabel.repaint();
            }
          }
       }
    }
    
    public class GUI extends JFrame {
        
        public Canvas myCanvas;
    
        public GUI(int w, int h) {
           this.setSize(w, h);
           this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    
           myCanvas = new Canvas(w, h);
           this.getContentPane().add(myCanvas);
           this.setVisible(true);
           this.pack();
        }
    }

    public class Canvas extends JPanel {
    
      public BufferedImage myImg;
      public Graphics2D g2d;
      public JLabel myLabel;
    
      public Canvas(int w, int h) {
          myImg = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
          myLabel = new JLabel(new ImageIcon(myImg));
          this.add(myLabel);
          g2d = myImg.createGraphics();
      }

      @Override
      public void paintComponent(Graphics g) {
          super.paintComponent(g);
          g2d.clearRect(0, 0, myImg.getWidth(), myImg.getHeight());
          g2d.setColor(Color.RED));
          g2d.fillPolygon(pointsX, pointsY, 3);
          g.drawImage(myImg, 0, 0, null);
      }
    }

This is how my object is flickering


Solution

  • You really need to take the time to read through:

    These aren't "beginner" topics and a reasonable understanding of Swing in general and the language in particular would be very advantageous.

    Don't, ever, use getGraphics on a component. This is simply a bad idea (and I have no idea why this method is public).

    enter image description here

    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.image.BufferedImage;
    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        JFrame frame = new JFrame();
                        frame.add(new TestPane());
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                    } catch (IOException ex) {
                        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private BufferedImage myImg;
            private double rotation;
    
            public TestPane() throws IOException {
                myImg = ImageIO.read(getClass().getResource("/images/happy.png"));
    
                Timer timer = new Timer(33, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        rotation += Math.toRadians(5);
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
    
                AffineTransform at = AffineTransform.getTranslateInstance((getWidth() - myImg.getWidth()) / 2, (getHeight() - myImg.getHeight()) / 2);
                at.rotate(rotation, myImg.getWidth() / 2, myImg.getHeight() / 2);
    
                g2d.transform(at);
                g2d.drawImage(myImg, 0, 0, this);
                g2d.dispose();
            }
        }
    }