javaawtjava-8doublebuffered

Continous double buffering solution not working


I'm attempting to double buffer an image containing a polygon in the method paint() using AWT. Using an Image object for the buffering process, I set the image background to black, draw the polygon to the image and then draw the buffered image to the screen. I then call repaint() in order to render the image again.

Unfortunately, I'm still receiving artifacts when repainting the image. What am I doing incorrectly?

EDIT: As a side note, I'm using Java 8. EDIT 2: I'm calling repaint() in paint() because I need to continuously buffer the image. The polygon is meant to translate across the screen based off user input.

import java.applet.Applet;
import java.awt.*;

public class DoubleBuffer extends Applet {
    int xSize = 900;
    int ySize = 600;

    Image bufferImage;
    Graphics bufferG;

    @Override
    public void init() {
        this.setSize(xSize, ySize);

        //Double buffering related variables
        bufferImage = this.createImage(xSize, xSize);
        bufferG = bufferImage.getGraphics();
    }

    //BUFFERING DONE HERE
    @Override
    public void paint(Graphics g){
        //drawing images to external image first (buffering)
        bufferG.setColor(Color.BLACK);
        bufferG.fillRect(0,0,xSize,ySize);
        bufferG.setColor(Color.WHITE);
        bufferG.drawRect(100, 100, 100, 100);

        //draw the image and call repaint
        g.drawImage(bufferImage, 0, 0, this);
        repaint();
    }
}

Solution

  • The problem is that you are not overriding update which is the method which will be called in response to repaint requests. For heavy-weight components, the default implementation of update will first clear the component to the background color (may default to white) then invoke your paint method.

    As pointed out by others, you shouldn’t call repaint from within a paint method. You should use a Timer.

    After cleaning up, the entire class will look like:

    public class DoubleBuffer extends Applet {
        int xSize = 900;
        int ySize = 600;
    
        Image bufferImage;
        Graphics bufferG;
        Timer timer=new Timer(200, ev->repaint());
    
        @Override
        public void init() {
            this.setSize(xSize, ySize);
        }
    
        @Override
        public void addNotify() {
            super.addNotify();
            //Double buffering related variables
            bufferImage = this.createImage(xSize, xSize);
            bufferG = bufferImage.getGraphics();
            timer.start();
        }
    
        @Override
        public void removeNotify() {
            super.removeNotify();
            bufferImage = null;
            bufferG = null;
            timer.stop();
        }
    
    
        //BUFFERING DONE HERE
        @Override
        public void paint(Graphics g){
            //drawing images to external image first (buffering)
            bufferG.setColor(Color.BLACK);
            bufferG.fillRect(0,0,xSize,ySize);
            bufferG.setColor(Color.WHITE);
            bufferG.drawRect(100, 100, 100, 100);
    
            //draw the image and call repaint
            g.drawImage(bufferImage, 0, 0, this);
        }
    
        @Override
        public void update(Graphics g) {
            // now not clearing the background as we expect paint() to fill the entire area
            this.paint(g);
        }
    }