The goal of this discussion is to illustrate the concept of double buffering as a technique for eliminating flicker from the display. Program 1 will be shown in two versions. The first illustrates the basic logic for creating an off-screen image and then drawing that image as a whole on the screen. However, as will be seen, the program has considerable flicker. In the second version, the flicker is eliminated by a very simple step, but one that may not be obvious without a clear understanding of how the program and the Java Virtual Machine interact to display graphic data.
The example program draws 10 randomly placed lines of randomly selected colors on an offscreen image. It then displays the image. After that, it adds 10 more lines, displays the updated image, etc., until the program is terminated.
The image is displayed by calling the
repaint
method, a method inherited by the applet from theComponent
class. It calls theupdate
method which, in turn, calls thepaint
method. Typically, programs overridepaint
, and code is included to do the actual drawing or display.The process just described is illustrated in the program that follows. You may stop it and start it by pressing the mouse button in the display.
import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class DoubleBufa extends Applet implements Runnable, MouseListener { Thread runThread = null; Dimension d; Image offscreenImage; Graphics offscreenImageG; int rd, gr, bl; int x1, y1, x2, y2; Color c; Color backGround; int imageWidth, imageHeight; Random random; // ************* init public void init () { random = new Random (); d = this.getSize (); offscreenImage = this.createImage ( d.width, d.height ); offscreenImageG = offscreenImage.getGraphics (); offscreenImageG.setColor ( Color.white ); offscreenImageG.fillRect ( 0, 0, d.width, d.height ); this.addMouseListener ( this ); } // end init // ************* start public void start () { if ( runThread == null ) { runThread = new Thread ( this ); } // end if runThread.start (); } // end start // ************* stop public void stop () { if ( runThread != null ) { runThread.stop (); } // end if } // end stop // ************* run thread public void run () { while ( true ) { drawLines (); // draws 10 lines on offscreenImage try {Thread.sleep ( 50 );} catch ( InterruptedException e) {;} repaint (); } // end while } // end run // ************* paint public void paint ( Graphics g ) { g.drawImage ( offscreenImage, 0, 0, Color.white, this ); } // end paint // ************* draw Lines public void drawLines () { for ( int i = 0; i < 10; i++ ) { rd = (int)( 255 * random.nextFloat () ); gr = (int)( 255 * random.nextFloat () ); bl = (int)( 255 * random.nextFloat () ); x1 = (int)( d.width * random.nextFloat () ); y1 = (int)( d.height * random.nextFloat () ); x2 = (int)( d.width * random.nextFloat () ); y2 = (int)( d.height * random.nextFloat () ); offscreenImageG.setColor ( new Color ( rd, gr, bl ) ); offscreenImageG.drawLine ( x1, y1, x2, y2 ); } // end for } // end drawLines //*********** Interface Methods *********** //**** WindowListener methods public void windowActivated ( WindowEvent e ) { } public void windowDeactivated ( WindowEvent e ) { } public void windowOpened ( WindowEvent e ) { } public void windowClosed ( WindowEvent e ) { } public void windowClosing ( WindowEvent e ) { this.hide (); System.exit (0); } public void windowIconified ( WindowEvent e ) { } public void windowDeiconified ( WindowEvent e ) { } //**** MouseListener methods boolean running = false; public void mousePressed ( MouseEvent e ) { if ( running ) { runThread.suspend (); running = false; } else { runThread.resume (); running = true; } } // end mousePressed public void mouseReleased ( MouseEvent e ) { } // end mouseReleased public void mouseEntered ( MouseEvent e ) { } // end mouseEntered public void mouseExited ( MouseEvent e ) { } // end mouseExited public void mouseClicked ( MouseEvent e ) { } // end mouseClicked } // end DoubleBufRun the applet
Notice that in spite of the double buffering the program produces considerable flicker. In a slightly modified version of the program, below, flicker is eliminated.
The second version of the program eliminates the flicker by overriding the
update
method. To understand why this works, you need to understand the calling sequence generated byrepaint
and what each method does in that sequence. The best way to do this is to look at their definitions in theComponent
class.
The critical issue in many programs, and the case in this example, is that
update
assumes the background has not been cleared, so it clears it before callingpaint
, which draws on the screen, displays an image, or whatever. It is this sequence of clearing, displaying an image, clearing, displaying, etc. that produces the flicker.One way to eliminate the flicker is to override
update
and immediately call thepaint
method without clearing the background. That is what is done in the second version of the example program, below. The overriddenupdate
method is shown in bold. To shorten the program, the interface methods have been deleted; they are the same as those included in the first version of the example, above.import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class DoubleBufb extends Applet implements Runnable, MouseListener { Thread runThread = null; Dimension d; Image offscreenImage; Graphics offscreenImageG; int rd, gr, bl; int x1, y1, x2, y2; Color c; Color backGround; int imageWidth, imageHeight; Random random; // ************* init public void init () { random = new Random (); d = this.getSize (); offscreenImage = this.createImage ( d.width, d.height ); offscreenImageG = offscreenImage.getGraphics (); offscreenImageG.setColor ( Color.white ); offscreenImageG.fillRect ( 0, 0, d.width, d.height ); this.addMouseListener ( this ); } // end init // ************* start public void start () { if ( runThread == null ) { runThread = new Thread ( this ); } // end if runThread.start (); } // end start // ************* stop public void stop () { if ( runThread != null ) { runThread.stop (); } // end if } // end stop // ************* run thread public void run () { while ( true ) { drawLines (); // draws 10 lines on offscreenImage try {Thread.sleep ( 50 );} catch ( InterruptedException e) {;} repaint (); } // end while } // end run // ************* update public void update ( Graphics g ) { paint ( g ); } // end update // ************* paint public void paint ( Graphics g ) { g.drawImage ( offscreenImage, 0, 0, Color.white, this ); } // end paint // ************* draw Lines public void drawLines () { for ( int i = 0; i < 10; i++ ) { rd = (int)( 255 * random.nextFloat () ); gr = (int)( 255 * random.nextFloat () ); bl = (int)( 255 * random.nextFloat () ); x1 = (int)( d.width * random.nextFloat () ); y1 = (int)( d.height * random.nextFloat () ); x2 = (int)( d.width * random.nextFloat () ); y2 = (int)( d.height * random.nextFloat () ); offscreenImageG.setColor ( new Color ( rd, gr, bl ) ); offscreenImageG.drawLine ( x1, y1, x2, y2 ); } // end for } // end drawLines } // end DoubleBuf
Run the applet