Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Double buffer in standard Java AWT

0.00/5 (No votes)
16 Apr 2002 1  
Create a double buffered base-class using standard Java AWT.

Introduction

Have you ever created an application that showed an animation in your window? You probably have, and if you have not, you probably will someday. The coding is really simple: you take a Panel and you add a thread to it that calls the repaint method every x milliseconds. Done?

No, it's not quiet over yet. Because, if you leave it like that, the screen will flash and flicker, and your animation will end up partially painted, or not painted at all.

The solution for this is the infamous double buffer.

AWT versus SWING

In the Swing framework, you can add the double buffer just by setting this property to true in your class. Swing is carrying out all of the tasks needed for you. They're behind the scenes, and all that you have to know is that it's done. The flicker is gone. Thank you mister Sun.

But what if you use the standard AWT? The Swing framework is rather heavy and still has some nasty bugs, so, on older machines, it's more likely that you use the standard AWT. But, there's no double buffer here, isn't it?

I've studied this problem for some while now, and this is the solution I've come up with. It is a solution, not the solution. I've seen others too, but they all have more or less the same components.

The DoubleBuffer Class

Since we are good Java programmers, we create a base-class for this, so we can implement the double buffer functionality whenever we feel like. You can choose between the classes Component, Container, Canvas and Panel to extend, but I suggest you choose the Panel class. If you choose another, you can get problems later on, with some event listeners that will not fire properly.

import java.awt.*;

public class DoubleBuffer extends Panel{

    public DoubleBuffer(){
        super();
}

Eliminating the Flicker

What's causing the flicker? Well, if you call the repaint method, you actually tell the VM to repaint this component as soon as possible. You can't tell when (but you can set an initial delay). When the VM has time to carry out the paint tasks, it calls the update method for you. And there's the first problem already. The update method clears the panel for you, so you can paint to it, without concerning about the background. That's what's causing the flicker. It clears the panel, shows in the window, paints the panel and shows again. So every paintjob has in fact two jobs, clearing and painting.

We're going to deprecate this.

    public void update(Graphics g){
        paint(g);

    public void paint(Graphics g){
    }

Now, the background isn't cleared anymore, so the flicker is gone. If you use this to animate through some images with the same dimensions, you are finished here already, because you don't have to clear the previous image.

But what if you have to clear the previous paintjob?

The Buffer Image

The idea behind double buffering is that you first paint everything to an off-screen image, and, when it's ready, paint it to the screen in just one paintjob. For this purpose, we need a bufferimage and it's brother buffergraphics. This image has always the same dimensions as your panel, so if your panel resizes, the image has to resize also, and the previous has to be cleared out of memory.

    //    class variables

    private int bufferWidth;
    private int bufferHeight;
    private Image bufferImage;
    private Graphics bufferGraphics;

    public void paint(Graphics g){
        //    checks the buffersize with the current panelsize

        //    or initialises the image with the first paint

        if(bufferWidth!=getSize().width || 
          bufferHeight!=getSize().height || 
          bufferImage==null || bufferGraphics==null)
            resetBuffer();
    }

    private void resetBuffer(){
        // always keep track of the image size

        bufferWidth=getSize().width;
        bufferHeight=getSize().height;

        //    clean up the previous image

        if(bufferGraphics!=null){
            bufferGraphics.dispose();
            bufferGraphics=null;
        }
        if(bufferImage!=null){
            bufferImage.flush();
            bufferImage=null;
        }
        System.gc();

        //    create the new image with the size of the panel

        bufferImage=createImage(bufferWidth,bufferHeight);
        bufferGraphics=bufferImage.getGraphics();
    }

So, your bufferimage has been created and now it's ready to picasso. We add some functionality to the paint method and create a paintbuffer method. After that the off-screen image has to be copied to the screen. Everything is painted on the screen in one go, so bye bye flicker.

    public void paint(Graphics g){
        ...    resetBuffer();

        if(bufferGraphics!=null){
            //this clears the offscreen image, not the onscreen one

            bufferGraphics.clearRect(0,0,bufferWidth,bufferHeight);

            //calls the paintbuffer method with 

            //the offscreen graphics as a param

            paintBuffer(bufferGraphics);

            //we finaly paint the offscreen image onto the onscreen image

            g.drawImage(bufferImage,0,0,this);
        }
    }

    public void paintBuffer(Graphics g){
        //in classes extended from this one, add something to paint here!

        //always remember, g is the offscreen graphics

    }

Now, your double buffer class is ready!

Extending the new class

Now, everything you have to do is to extend DoubleBuffer and deprecate the paintBuffer method instead of the paint method. In your application, you can call the repaint method whenever you feel like, and the screen is not going to flicker once.

public class MyBuffer extends DoubleBuffer{

    private int posX;

    public MyBuffer(){
        super();

        posX=0;
    }

    public void animateToTheRight(){
        //    this can be called from everywhere, anytime

        posX++;
        repaint();
    }

    //    attention: we don't touch update() and paint() anymore

    //    we use paintbuffer() instead


    public void paintBuffer(Graphics g){
        /// g is the offscreen graphics

        g.drawString("Double Buffer!",posX,20);
    }
}

In the example, if you add a thread and let it call animateToTheRight(), a string is scrolling from left to right, without causing any flicker.

Voila, the double buffer is working.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here