GuidesDouble-Buffer Applet

Double-Buffer Applet

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


Introduction

If you’ve ever written an applet that does a lot of graphics, you might have noticed a flickering whenever the applet is redrawn. You were probably annoyed by it, too. This article tells you how to get rid of it.

Problem Definition

We’re talking here about custom-drawn graphics — that is, lines, points, rectangles, and images, rather than GUI components like buttons and text fields. These custom-drawn graphics are carried out by making calls on the Graphics object passed into

Applet.paint()
.

Just before

paint()
is called, the applet window is cleared. This is necessary because otherwise, each time you drew, you would be drawing over the previously drawn image.

Just after the applet is cleared, but before your stuff is redrawn, the applet is an empty square. After your stuff is redrawn, the applet is full of stuff. If this sequence of events happens often enough and/or if the delay between clearing and drawing is long enough, you’ll see a flickering.

The

paint()
Method

Applet.paint()
is a method that you create to carry out whatever drawing is needed for your application. It is called each time the applet needs to be displayed. This happens when you have uncovered the applet by moving some other window out of the way or have de-iconified the window. It happens when the applet first starts up, as well. The idea is to make sure that, any time the applet is visible, the drawing routines are called to fill the visible area with the right stuff.

Your applet will also cause itself to be repainted, even if it hasn’t just become visible. This can happen in response to user input, or it can happen on its own.

The tricky thing about

paint()
is that you don’t really call it. Rather, “the system” calls it when it thinks that the applet needs to be redrawn. The system decides this is necessary when the applet has become newly visible in some way.

The applet can decide for itself that it wants to be redrawn. It tells “the system” this by calling

repaint()
; this method doesn’t call

paint()
directly, but it does make sure that “the system” will call it very soon.

When the system does decide, for whatever reason, that it’s time to redraw, it creates a Graphics object for the applet’s visible area. This object is specific to just this area of the window, and it contains all the drawing methods you might want to use. This Graphics object is passed to

paint()
, which uses it to do the actual drawing.

Making It Invisible

The basic problem here is this Graphics object. Whenever one of its drawing methods (such as

Graphics.drawLine()
) is called, the visual results are seen immediately. As your

paint()
method makes various calls, each one is carried out in turn. If you could speed up your vision, you could see each thing being drawn. This is bad, because it breaks the illusion of smooth animation that you are trying to create with your graphics. In particular, the act of clearing the applet square is a major eyesore.

The solution is to draw these things somewhere where they can’t be seen, and then copy everything to the screen at once.

This is called double buffering. The name comes from the old days, back when our code drew directly to the screen instead of to something called a “window”. Back then, if you wanted to avoid flicker, you would draw to a buffer — an area of memory — that wasn’t on-screen, and then point the screen hardware at that buffer when you were done. While the screen hardware was pointed there, you could draw the next frame to the first buffer. By using two buffers instead of one, you hid the process of drawing.

We use a somewhat different metaphor in Java, since we’re talking about windows rather than screens. In this case, we’re going to have two Graphics objects. One of the them is the original we mentioned earlier — the one where, if you draw to it, you immediately see the results right on the screen. We’ll call this our on-screen buffer. The other Graphics object is our off-screen buffer. When we draw here, the process won’t be observed by the user; the drawing will be invisible until we explicitly transfer the results to our on-screen buffer.

paint()
and

update()

I mentioned before that the applet window is cleared for you before you draw, and that this is something that’s bothering us. Luckily, however, Java is object-oriented, which means that we can often change the behavior of the system by overriding a method.

In this case, we can take charge of the whole applet-clearing situation by overriding the

update()
method.

update()
and

paint()
are closely related, and both are designed explicitly to be overriden by the user. However,

paint()
is overridden far more often than

update()
is.

paint()
needs to be overridden any time you want your applet to draw something.

paint()
makes the actual graphics calls, and the default

paint()
does nothing. You override it to make it do something.

paint()
is overridden in any applet that does drawing.

update()
, on the other hand, is responsible for defining the buffering policy, and is rarely changed by the applet programmer.

Here’s how the two work together, normally:

  1. Applet code calls
    repaint()


  2. repaint()
    tells the system to eventually cause a redraw

  3. After a while, the system calls
    Applet.update()


  4. Applet.update()
    clears the applet square


  5. Applet.udpate()
    calls

    Applet.paint()


  6. Applet.paint()
    draws a bunch of stuff

Drawing to an Image

We don’t like this because steps 4 and 5 happen right on-screen, and so we get flickering.

Instead, we want these steps to be done off-screen. We’re going to create an Image object that is the same size as the applet. This Image is a visual component, but it’s not going to displayed directly anywhere — that is, it’s not a sub-component in any on-screen component. It’s just going to be an off-screen object that we can draw into.

Note that an Image object is different than a Graphics object. An Image is something that you can display; a Graphics is something that you can draw into. But the key here is that an Image object has a Graphics object. When you want to draw to an Image, you ask the Image for its graphics object, using

Image.getGraphics()
.

  1. Applet code calls
    repaint()


  2. repaint()
    tells the system to eventually cause a redraw

  3. After a while, the system calls
    Applet.update()


  4. Applet.update()
    grabs an offscreen Image


  5. Applet.update()
    gets a Graphics object from this Image


  6. Applet.update()
    clears this offscreent buffer


  7. Applet.update()
    calls

    Applet.paint()
    on the offscreen buffer


  8. Applet.update()
    copies the Image to the screen

Implementation

This stuff is kind of tricky to figure out, because it involves understanding the various methods in the AWT display system, and what each one is for.

One you’ve figured this out, however, the code changes are fairly small. All we really have to do is change

update()

paint()
can be left alone. Which is good, because that’s a method that should only be doing drawing, not fancy buffer gymnastics.

Here’s the code for the default version of

update()
, taken directly from the Java 1.1 source:

Listing 1: The default version of update().


if ((! (peer instanceof java.awt.peer.LightweightPeer)) &&
(! (this instanceof Label)) && (! (this instanceof TextField))) {

// We’re drawing the background, so use the background colo
g.setColor(getBackground());

// Cover the entire area, to clear it
g.fillRect(0, 0, width, height);

// Restore the foreground color
g.setColor(getForeground());
}
// Let paint() do its stuff
paint(g);

Ignoring all the junk about peers, you can see that this is pretty simple. We set the current color to be the background color, because the call to

fillRect()
is going to draw a nice, monochrome background. After restoring the current color, we call

paint()
to let it do its dirty work.

In our double-buffered version, it’s a little different:

Listing 2: The double-buffered version of update().


// offscreen is our off-screen buffer — an Image object
// Get its graphics object, for drawing
Graphics gg = offscreen.getGraphics();

// Clear it, as before; but remember, this is off-screen!
gg.setColor( getBackground() );
gg.fillRect( 0, 0, width, height );
gg.setColor( getForeground() );

// Let paint() draw to this, again not visibly on-screen
paint( gg );

// We don’t need this Graphics object anymore
gg.dispose();

// Copy the contents of the Image to the on-screen area
g.drawImage( offscreen, 0, 0, null );

The Abstraction

In the sample code, we’ve created a class called DoubleBufferApplet. This class does double buffering “automatically” — that is, it contains all of the above logic in its

update()
method.

It’s easy for an applet programmer to make use of this. Normally, you’d create an object that subclasses from Applet. Instead, you subclass from

DoubleBufferApplet
. That’s all you have to do! The same program that was flashing horribly just a little while ago is now, well, not flashing horribly.

Take a Look

To see this in action, take a look here. This shows two instances of the same applet; one is using double buffering, and one is not.

Problems/Ideas

The major problem with the framework outlined above is that we’re subclassing our applet from a special applet base class that gives us the double-buffering functionality. However, it’s not ideal to get this by subclassing; after all, we might have another applet base class that we want or need to subclass from, and you can’t subclass from both.

A better framework might be to create a

DoubleBufferCanvas
that does the same thing as

DoubleBufferApplet
, and subclass from that. Such a canvas can then be put into any kind of applet.

Source Code

About the Author

Greg Travis is a freelance programmer living in New York City. His interest in computers can probably be traced back to the episode of "The Bionic Woman" where Jamie runs around trying to escape a building, whose lights and doors are controlled by an evil artificial intelligence. He’s a devout believer in the idea that when a computer program works, it’s a complete coincidence. He can be reached at mito@panix.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories