'double buffering' Sample program in J2ME

By: Charles Viewed: 170 times  Printer Friendly Format    


The term double buffering refers to a technique for buffering a graphics context before displaying it. The idiom requires that you use two graphics contexts -or buffers - hence its name.

You first draw graphics in a secondary graphics context, and later copy its contents to the graphics context that represents the device's display. This secondary graphics context is called an off-screen buffer. An off-screen buffer is one that doesn't render to the display.

The motivation for this technique is performance. Drawing operations can result in frequent updates to the display, causing the user to perceive display flicker. To avoid flicker, you first perform your drawing to an off-screen graphics context, then copy the whole off-screen graphics context to the original device graphics. A copy operation is usually faster than the multitude of drawing operations required of even a relatively complex canvas, so it can be done with almost no perceptible flicker.

The sample program below demonstrates the use of double buffering. It performs some simple drawing functions in an off-screen buffer, then copies the contents of that buffer to the primary graphics context that represents the device's display. Although the drawing routines in this example are relatively simple, a real-life application might perform much more complicated drawing, truly warranting the need for double buffering.

/* Double buffering uses two graphics contexts. The only way to obtain a
second graphics context in MIDP is through the Image class.*/
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import java.io.IOException;

/**
Demonstrates double buffering of graphics context for
display on a Canvas.
*/
public class DoubleBufferDemo extends Canvas
implements CommandListener
{
// A constant that represents the color white.
private static final int WHITE =
0xFF << 16 | 0xFF << 8 | 0xFF;
private static Command back =
new Command("Back", Command.BACK, 1);
GraphicsDemo gDemo = GraphicsDemo.getInstance();
private Display display = Display.getDisplay(gDemo);
// The image object to use to obtain an off-screen
// Graphics object.
private Image offscreen;
// A variable used for determining if the implementation
// is automatically double buffered. Holds the value
// true if the implementation automatically does double
// buffering, false otherwise.
private boolean autoDoubleBuffered = true;

/**
No-arg constructor.
*/
public DoubleBufferDemo()
{
super();
addCommand(back);
setCommandListener(this);
display.setCurrent(this);
if (!isDoubleBuffered())
{
// If the implementation doesn't automatically
// use double buffering, get an Image so we
// can get an off-screen Graphics from it.
// This Image is mutable! Its dimensions are the
// width and height of this Canvas.
offscreen = Image.createImage(getWidth(), getHeight());
autoDoubleBuffered = false;
}
}
protected void paintClipRect(Graphics g)
{
int clipX = g.getClipX();
int clipY = g.getClipY();
 

int clipH = g.getClipHeight();
int clipW = g.getClipWidth();
int color = g.getColor();
g.setColor(WHITE);
g.fillRect(clipX, clipY, clipW, clipH);
g.setColor(color);
}
public void paint(Graphics g)
{
Graphics originalG = null;
int width = getWidth();
int height = getHeight();
if (!autoDoubleBuffered)
{
// Save original graphics context and get a new
// off-screen Graphics from the utility Image.
originalG = g;
g = offscreen.getGraphics();
// Clear the clip rectangle using the new Graphics
// object. This way we're using double buffering
// to clear the Canvas, thereby avoiding
// flickering. Clearing the Canvas is drawing
// like all other drawing operations.
paintClipRect(g);
}
else
{
// Clear the Canvas with the original graphics
// because the implementation does double
// buffering automatically.
paintClipRect(g);
}
for (int x = 0, y = 0; (x < width / 2); x = x + 2)
{
g.drawRect(x, y, (width - x) - x, (height - y) - y);
y++; y++;
}
// Drawing the image actually copies the contents of
// the image's off-screen Graphics context to the
// device's Graphics context.
if (!autoDoubleBuffered)
{
originalG.drawImage(offscreen, 0, 0,
Graphics.TOP | Graphics.LEFT);
}
}
public void commandAction(Command c, Displayable d)
{
if (c == back)
{
GraphicsDemo.getInstance().display();
}
}
 

}

The constructor contains the first code related to double buffering. The statement below, taken from the DoubleBufferDemo no-argument constructor, determines whether the implementation automatically supports double buffering.

if (!isDoubleBuffered())
{
offscreen = Image.createImage(getWidth(), getHeight());
autoDoubleBuffered = false;
}

If the implementation does support double buffering, the application doesn't need to execute it. The Canvas.isDoubleBuffered() method tells you whether the implementation does double buffering implicitly. Notice the construction of the Image object. This call to Image.createImage() produces a mutable Image object. The application needs a mutable Image because it will perform its drawing in the Image object's Graphics context, which is the off-screen buffer we need. This is the only way to obtain an additional Graphics in MIDP.

The paint() method contains the rest of the double-buffering code. If there is no automatic double buffering, the application must perform it. This requires a second graphics context. The following fragment from the paint() method demonstrates the idiom.

public void paint(Graphics g)
{
...
if (!autoDoubleBuffered)
{
originalG = g;
g = offscreen.getGraphics();
}
else
{
paintClipRect(g);
}
...
}

A temporary variable saves a reference to the original Graphics object, which represents the device's graphics context. The new graphics context is obtained through the Image object constructed earlier. This Graphics is associated with the Image

Now the paint(Graphics g) method performs its drawing operations to the off-screen Graphics context. When it's done, it copies the contents of the off-screen Graphics to the original Graphics context, which results in rendering to the display. The copy operation is done by the call to the Graphics.drawImage() method. This method says, in effect, "Copy the contents of this image argument's graphics context to me."

The MIDP double buffering mechanism differs from Swing's double buffering. In Swing, you can double-buffer drawing operations on any Component, not just Canvas objects. Swing applications call java.awt.Component.getGraphics() to obtain an off-screen graphics context. The application can draw in this context. It then sets this off-screen graphics context to be the one associated with the native device.

MIDP has no such call. The only reference to a Graphics object that's associated with the native device is the one passed to the paint(Graphics g) method of a Canvas.



Most Viewed Articles (in J2ME )

Latest Articles (in J2ME)

Comment on this tutorial