Reducing Flicker

This example derived from Exploring Java, 2nd ed, chapter 16, shows an image with "terrible flicker" as it is moved:

TerribleFlicker.java

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class TerribleFlicker extends Applet
   implements MouseMotionListener{

  int grid = 10;
  int currentX, currentY;
  Image img;

  public void init() {
    img=getImage(getCodeBase(),"monaSmall.gif" );
    addMouseMotionListener(this);
  }

  public void mouseDragged( MouseEvent e ) {
    currentX = e.getX();
    currentY = e.getY();
    repaint();
  }
  //Have to override mouseMoved with empty method.
  public void mouseMoved ( MouseEvent e){}
 
  public void paint( Graphics g ) {
    int w = getSize().width/grid;
    int h = getSize().height/grid;
    boolean black = false;

    // Draw the checkerboard background
    for ( int y = 0; y <= grid; y++ )
      for ( int x = 0; x <= grid; x++ ) {
        g.setColor((black = !black) ? Color.black
                               : Color.white );

        g.fillRect( x * w, y * h, w, h );
      }
    // then the image
    g.drawImage( img, currentX, currentY, this );
  }
}

Since we redraw the whole applet anyway, no need for update to waste time clearing it. So override it with an update that just calls paint():

UpdateOverride.java

import java.awt.*;
import java.awt.event.*;
java.applet.*;

public class UpdateOverride extends Applet

...
... 
//Same as TerribleFlicker above except update
// is overriden

  public void update ( Graphics g ){

        paint(g);
}
 
  public void paint( Graphics g ) {
...

Finally, we use the most powerful method for eliminating flicker: the double buffer.

The process of sending drawing instructions to the screen involves a lot of overhead. There are lots of communications calls between the AWT and the host that take time.

By doing the drawing all onto an image within Java and then just sending the whole image in one call, there is a considerable speed up in the drawing.

Below we create an image and then get its graphics context object for doing the drawing onto it. Once this image is ready, we just draw it to the screen in one call.

Note that once a graphics context object is clipped, it cannot be unclipped. So we need to get a new one each time around.

DoubleBuffered.java

import java.awt.*;

public class DoubleBufferedClipped extends Clipped {
  Image offScrImg;

  public void update( Graphics g ) {
  // Create an offscreen image and then get its
  // graphics context

   if ( offScrImg == null )
     offScrImg =
       createImage( getSize().width,
                    getSize().height );

   Graphics og = offScrImg.getGraphics();

   // Do the clipping on both the off
   // and on screen graphics contexts.

   int lastX = currentX, lastY = currentY;
   currentX = nextX; currentY = nextY;
   clipToAffectedArea( og, lastX, lastY,
       currentX, currentY, imgWidth, imgHeight );
   clipToAffectedArea( g, lastX, lastY,
       currentX, currentY, imgWidth, imgHeight );

   // Now draw on the offscreen image.
   paint( og );

   // Don't bother to call paint,
   // just draw the offscreen image
   // to the screen.

   g.drawImage(offScrImg, 0, 0, this);

   // Get rid of the offscreen graphics context.
   // Can't unclip a graphics context so have
   // to get a new one next time around.

   og.dispose();
  }
}


Maintained by John Loomis, last updated 17 July 2003