Example Program 1

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 the Component class. It calls the update method which, in turn, calls the paint method. Typically, programs override paint, 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 DoubleBuf

Run 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 by repaint and what each method does in that sequence. The best way to do this is to look at their definitions in the Component 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 calling paint, 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 the paint method without clearing the background. That is what is done in the second version of the example program, below. The overridden update 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