Please send questions to st10@humboldt.edu .
/**
 * TrainWiDblBuf
 * 
 * yet-another Thread demo that is the first using "double buffering"
 * to improve animation using threads in an AWT applet.
 *
 * It uses an AWT Applet and the Runnable interface, also,
 * and animates an image of a train moving left to right across
 * the applet area.
 *
 * Expects: train.gif to be in the same directory as its .class file
 *
 * comments marked with [1] are from Chapter 4, "Graphics", section 
 * "Animation with Double-Buffering", from Java Examples in a Nutshell, FIRST 
 * EDITION, Flanagan, O'Reilly, 1997, pp. 64-69
 *
 * RECOMMENDED SIZE: height 200, width 600
 *
 * modified by: Sharon M. Tuttle
 * last modified: 3-26-01
 **/

// A SUMMARY OF SOME IMPORTANT POINTS ABOUT DOUBLE-BUFFERING:
// 
// *   need a "buffer": that'll be a java.awt.Image object
// *   need to create this "buffer" using Component.createImage();
// *   this buffer needs a Graphics object to draw to: Image.getGraphics()
//                      can get the Graphics object for this buffer
// *   you draw to the buffer's Graphics object, rather than to the Graphics
//                      object for the applet;
// *   when the drawing of the off-screen image is complete, you copy the off-screen
//                      image onto the applet screen all at once
// *   to prevent applet's default update() method from clearing the screen before 
//                      calling paint() as it does whenever repaint() is called, you
//                      need to override update() so that it just calls paint()

import java.applet.Applet;
import java.awt.*;

public class TrainWiDblBuf extends Applet implements Runnable
{
        int       x, y;
        Image     myTrain;
        Thread    myRunner;

        Image     buffer;          // [1]"the off-screen image for double-buffering"
        Dimension appletSize;      // [1]"the size of the applet"
        Graphics  bufferGraphics;  // [1]"a Graphics object for the buffer"

        public void init()
        {
                setBackground(Color.lightGray);
                System.out.println("Train with Double Buffering, Version 1.1");
                Label yourName = new Label("CIS 480 - Spring 2001");
                add(yourName);
                Label describe = new Label("Train with Double Buffering: Version 1.1");
                add(describe);

                // currently in same directory as my .class file
                myTrain = getImage(getCodeBase(),"train.gif");

                // hard code the y pixel location
                y = 100;

		// initialize x to 0
		x = 0;

                //-----
                // [1] "set up an off-screen image for double-buffering"
                //-----

                // get size of applet (appletSize is a Dimension object)
                appletSize = this.getSize();

                // buffer is now created to be the same size as the applet
                buffer = this.createImage(appletSize.width, appletSize.height);

                // get a Graphics object for buffer
                bufferGraphics = buffer.getGraphics();
        }

        public void start()
        {
                if (myRunner == null)
                { 
                        // not bothering to name the thread, this time;
                        myRunner = new Thread(this);
                        myRunner.start();
                }
        }

        public void run()
        {
                Thread  executingThread;
                                
                executingThread = Thread.currentThread();

                while (myRunner == executingThread)
                {
                        x = 0;
                        repaint();
                        try
                        {
                                // sleep for 1 second (1000 msec = 1 sec)
                                Thread.sleep(1000);
                        }
                        catch(InterruptedException e) 
                        {
                        } 

                        for (int k = 0; k < 120; k++)
                        {
                                x = k * 5;
                                repaint();
                                try
                                {
                                        // pause for .1 second between
                                        // repaintings
                                        Thread.sleep(100);  // .1 second pause
                                }
                                catch (InterruptedException e) 
                                {
                                }
                        }  //end for
                }  //end while
        }

        //suspends execution when user leaves page
        public void stop()
        { 
                if (myRunner != null)
                {
                        myRunner = null;
                }
        }

        // VERY IMPORTANT --- [1] "It is important to override this method
        // like this for double-buffering" --- this new version of update()
        // doesn't clear the screen, it just calls paint() immediately.
        public void update(Graphics g)
        {
                paint(g);
        }

        // now, when double-buffering, here in paint() I will not paint on
        // Graphics object g --- I will paint on my buffer's Graphics object,
        // instead
        public void paint(Graphics g)
        {
                bufferGraphics.drawImage(myTrain, x, y, this);

                // [1]"then copy the off-screen buffer onto the screen"
                // (think: why 0,0 for coordinates, here?)
                g.drawImage(buffer, 0, 0, this);
        }
}