Previous | Next | Trail Map | Creating a User Interface | Working with Graphics

Eliminating Flashing: Implementing Double Buffering

The previous page showed you how to eliminate flashing by implementing the update() method. You might have noticed (depending on the performance of your computer) that the resulting applet, although it didn't flash, appeared to crawl a bit. That is, instead of the whole drawing area (or frame) being updated at once, sometimes one part would be updated before the part just to its right, causing noticeably uneven drawing between columns.

You can use double buffering to avoid this crawling effect by forcing the entire frame to be drawn at once. To implment double buffering, you need to create an undisplayed buffer (often called a backbuffer or offscreen buffer), draw to it, and then display the resulting image onscreen.

Here is the code for the graphics animation example, modified so that it implements double buffering. Below is the resulting applet in action.


Your browser can't run 1.0 Java applets. Here is a snapshot of what you'd see:

To create an offscreen buffer with the AWT, you need to first create an image of the proper size and then get a graphics context to manipulate the image. Below is the code that does this:

//Where instance variables are declared:
Dimension offDimension;
Image offImage;
Graphics offGraphics;
. . .
//In the update() method, where d holds the size of the 
//onscreen drawing area:
if ( (offGraphics == null)
  || (d.width != offDimension.width)
  || (d.height != offDimension.height) ) {
    offDimension = d;
    offImage = createImage(d.width, d.height);
    offGraphics = offImage.getGraphics();
}
Below, in bold font, is the new drawing code in the update() methods. Note that the drawing code now clears the entire background, but that it doesn't cause flashing because the code is drawing to the offscreen buffer, not to the screen. Note also that all the calls to fillRect() are performed to the offscreen buffer. The final result is drawn to the screen just before the update() method returns.
public void update(Graphics g) {
    ...//First, initialize variables and create the
       //offscreen buffer as shown above. Then erase the
       //previous image: 
        offGraphics.setColor(getBackground());
        offGraphics.fillRect(0, 0, d.width, d.height);
        offGraphics.setColor(Color.black);

    ...//Do everything the old paint() method did --
       //until we draw the rectangle.
            if (fillSquare) {
                offGraphics.fillRect(x, y, w, h);
                fillSquare = false;
            } else {
                fillSquare = true;
            }

    ...//The rest is exactly like the old paint() method
       //until the very end, when we add the following:
    //Paint the image onto the screen.
    g.drawImage(offImage, 0, 0, this);
}

It's not necessary that the update() method call the paint() method. All that's necessary is that the update() method somehow draw its offscreen buffer to the screen, and that the paint() method be able to draw the proper image when it's called directly by the AWT.

You might wonder why the offscreen image and graphics context are created in the update() method, instead of in (for example) the start() method. The reason is that the image and graphics context depend on the size of the applet Panel's drawing area, and the size of any Component's drawing area is not valid until the Component is just about to be drawn to the screen for the first time.


Previous | Next | Trail Map | Creating a User Interface | Working with Graphics