Flicker Free Graphics



The Mobile Information Device Profile (MIDP) defines low-level user interface classes for drawing directly on a device's display. The Using the MIDP Low-Level User Interface API tip covered how to use these classes to do basic drawing. This tip builds on that foundation and discusses how to use double buffering to draw flicker-free graphics.

The term "double buffering" refers to a common technique used in computer graphics. If you consider a device's display to be a memory buffer into which drawing primitives write (the drawing primitives are the basic drawing methods such as drawLine and drawArc), with double buffering you draw into a second, offscreen memory buffer and then copy the entire contents of the second buffer into the display buffer. Copying from one buffer to another is a very fast operation on most devices, so that the display changes almost instantaneously. By comparison, directly drawing to a display sometimes causes users to see a flicker, as individual parts of the display are updated. Double buffering avoids this flickering by combining multiple individual drawing operations (that is, those to the offscreen buffer) into a single copy operation to the display buffer.

It's easy to do double buffering in the context of the Mobile Information Device Profile. You can use the Image class (all classes mentioned in this tip are in the javax.microedition.lcdui package) to create an offscreen memory buffer. You use the Graphics class, the same class used to draw on the display, to draw to the offscreen buffer. You also use the Graphics class to copy the contents of the offscreen buffer onto the display. Double buffering is implemented with just a few adjustments to your painting routines.

The first thing you need to do is determine if double buffering is even necessary. On some implementations, double buffering is automatically supported by the system. In other words, when the system calls your Canvas object's paint method, the Graphics object passed to the method is that of an offscreen buffer managed by the system; the object is not from the display buffer. The system then takes care of copying the offscreen buffer to the display. Checking if double buffering is supported is easy -- all you do is call the isDoubleBuffered method, like this:

public class MyCanvas extends Canvas {

    private Image offscreen = null;
    private int   height;
    private int   width;
        
    public MyCanvas(){
        height = getHeight();
        width = getWidth();

        if( !isDoubleBuffered() ){
            offscreen = Image.createImage( width, height );
        }
                
        ..... // other initialization as appropriate
    }
        
    ...... // other code, including paint method
}

Notice how if isDoubleBuffered returns false, the constructor creates an offscreen buffer of the same width and height as the canvas. If the display is double buffered, isDoubleBuffered returns true and the offscreen buffer is not created.

The offscreen buffer is created by calling one of the Image.createImage methods. There are four such methods, each of which does the following:

  • Loads images from the MIDlet suite's JAR file
  • Makes a copy of an existing image
  • Creates an image from raw binary data
  • Creates a blank image of a specific height and width

The last of these createImage methods is the one used for double buffering. The other three createImage methods cannot be used for double buffering because they create images that are immutable, that is, images that cannot be changed. Only the last createImage method, the one that takes width and height parameters, can be used to create mutable images. Once you have a mutable image, you can call its getGraphicsmethod to obtain a Graphics object that you can use to draw into the image's buffer, just like drawing on the display.

Of course, the real work occurs in the paint method. A simple paint routine might look like this:

protected void paint( Graphics g ){
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, width, height );
}

Most paint routines are much more complicated, especially if animation is involved. To implement double buffering, add a few lines of code before and after the existing painting code, like this:

protected void paint( Graphics g ){
    Graphics saved = g;
        
    if( offscreen != null ){
        g = offscreen.getGraphics();
    }
    
    g.setColor( 255, 255, 255 );
    g.fillRect( 0, 0, width, height );
        
    if( g != saved ){
        saved.drawImage( offscreen, 0, 0,
                         Graphics.LEFT | Graphics.TOP );
    }
}

Basically all you're doing is obtaining the Graphics object for the offscreen buffer and using it to do the painting. At the end, the entire content of the offscreen buffer is copied to the display. Notice that this is done only if double buffering is not automatically supported. You can easily determine this by checking to see if an offscreen buffer has been allocated. If double buffering is automatically supported, you simply draw directly onto the display as usual.

Double buffering is not without its price. If you're only making small changes to the display, it might be slower to use double buffering. In addition, image copying isn't very fast on some systems; on those systems flicker can can happen even with double buffering. And there is a memory penalty to pay for double buffering: the offscreen memory buffer can consume a large amount of memory, memory that you might not be able to spare. Keep the number of offscreen buffers to a minimum. You could free the offscreen buffer whenever the canvas is hidden, for example, and allocate it again when the canvas is shown again. This is easy to do by overriding the canvas' hideNotify and showNotify methods.


你可能感兴趣的:(J2ME)