Multithreaded Graphics and Double Buffering

ShipSimulation.java  Illustrates the basic approach of multithreaded graphics whereas a thread adjusts parameters affecting the appearance of the graphics and then calls repaint to schedule an update of the display.

 

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

public class ShipSimulation extends Applet implements Runnable {
  ...
  
  public void run() {
    Ship s;
    for(int i=0; i
   *  When the "stop" button is pressed, stop the thread and
   *  clear the Vector of circles.
   */

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == startButton) {
      if (circles.size() == 0) {
        // Erase any circles from previous run.
        getGraphics().clearRect(0, 0, getSize().width,
                                      getSize().height);
        animationThread = new Thread(this);
        animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius, deltaX,
                                          deltaY));
    } else if (event.getSource() == stopButton) {
      if (animationThread != null) {
        animationThread = null;
        circles.removeAllElements();
      }
    }
    repaint();
  }

  /** Each time around the loop, call paint and then take a
   *  short pause. The paint method will move the circles and
   *  draw them.
   */

  public void run() {
    Thread myThread = Thread.currentThread();
    // Really while animationThread not null
    while(animationThread==myThread) {
      repaint();
      pause(100);
    }
  }

  /** Skip the usual screen-clearing step of update so that
   *  there is no flicker between each drawing step.
   */

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

  /** Erase each circle's old position, move it, then draw it
   *  in new location.
   */

  public void paint(Graphics g) {
    MovingCircle circle;
    for(int i=0; i windowWidth) && (deltaX > 0)) {
      setDeltaX(-deltaX);
    }
    if ((y -radius < 0) && (deltaY < 0)) {
      setDeltaY(-deltaY);
    } else if((y + radius > windowHeight) && (deltaY > 0)) {
      setDeltaY(-deltaY);
    }
  }

  public int getDeltaX() {
    return(deltaX);
  }

  public void setDeltaX(int deltaX) {
    this.deltaX = deltaX;
  }

  public int getDeltaY() {
    return(deltaY);
  }

  public void setDeltaY(int deltaY) {
    this.deltaY = deltaY;
  }
}
*//
SimpleCircle.java A class to store the x, y, and radius of a circle. Also, provides a draw method to paint the circle on the graphics object.
import java.awt.*;

/** A class to store an x, y, and radius, plus a draw method.
 *
  */

public class SimpleCircle {
  private int x, y, radius;

  public SimpleCircle(int x, int y, int radius) {
    setX(x);
    setY(y);
    setRadius(radius);
  }

  /** Given a Graphics, draw the SimpleCircle
   *  centered around its current position.
   */

  public void draw(Graphics g) {
    g.fillOval(x - radius, y - radius,
               radius * 2, radius * 2);
  }

  public int getX() { return(x); }

  public void setX(int x) { this.x = x; }

  public int getY() { return(y); }

  public void setY(int y) { this.y = y; }

  public int getRadius() { return(radius); }

  public void setRadius(int radius) {
    this.radius = radius;
  }
}
*//
DoubleBufferBounce.java  An enhancement to the previous applet containing bouncing circles. In this case, double buffering is used to improve the animation; all incremental updating is done in an off-screen image and then the image is drawn to the screen.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

/** Bounce circles around on the screen, using double buffering
 *  for speed and to avoid problems with overlapping circles.
 *  Overrides update to avoid flicker problems.
 *
  */

public class DoubleBufferBounce extends Applet implements
                                      Runnable, ActionListener {
  private Vector circles;
  private int width, height;
  private Image offScreenImage;
  private Graphics offScreenGraphics;
  private Button startButton, stopButton;
  private Thread animationThread = null;

  public void init() {
    setBackground(Color.white);
    width = getSize().width;
    height = getSize().height;
    offScreenImage = createImage(width, height);
    offScreenGraphics = offScreenImage.getGraphics();
    // Automatic in some systems, not in others.
    offScreenGraphics.setColor(Color.black);
    circles = new Vector();
    startButton = new Button("Start a circle");
    startButton.addActionListener(this);
    add(startButton);
    stopButton = new Button("Stop all circles");
    stopButton.addActionListener(this);
    add(stopButton);
  }

  /** When the "start" button is pressed, start the animation
   *  thread if it is not already started. Either way, add a
   *  circle to the Vector of circles that are being bounced.
   *  


   *  When the "stop" button is pressed, stop the thread and
   *  clear the Vector of circles.
   */

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == startButton) {
      if (circles.size() == 0) {
        animationThread = new Thread(this);
        animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius, deltaX,
                                          deltaY));
      repaint();
    } else if (event.getSource() == stopButton) {
      if (animationThread != null) {
        animationThread = null;
        circles.removeAllElements();
      }
    }
  }

  /** Each time around the loop, move each circle based on its
   *  current position and deltaX/deltaY values. These values
   *  reverse when the circles reach the edge of the window.
   */

  public void run() {
    MovingCircle circle;
    Thread myThread = Thread.currentThread();
    // Really while animationThread not null.
    while(animationThread==myThread) {
      for(int j=0; j= NUMIMAGES) {
        index = 0;
      }

      parent.repaint();

      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        break;   // Break while loop.
      }
    }
  }
}
*//
TimedAnimation.java An applet that demonstrates animation of an image by using a Timer. Note that Timer is located in the javax.swing package.
import java.awt.*;
import javax.swing.*;

/** An example of performing animation through Swing timers.
 *  Two timed Dukes are created with different timer periods.
 *
  */

public class TimedAnimation extends JApplet {
  private static final int NUMDUKES = 2;
  private TimedDuke[] dukes;
  private int i, index;

  public void init() {
    dukes = new TimedDuke[NUMDUKES];
    setBackground(Color.white);
    dukes[0] = new TimedDuke( 1, 100, this);
    dukes[1] = new TimedDuke(-1, 500, this);

  }

  //  Start each Duke timer.

  public void start() {
    for (int i=0; i= NUMIMAGES) {
      index = 0;
    }
    parent.repaint();
  }

  // Public service to start the timer.
  public void startTimer() {
    timer.start();
  }

  // Public service to stop the timer.
  public void stopTimer() {
    timer.stop();
  }
}
**//

Permanent link to this article: http://bangla.sitestree.com/multithreaded-graphics-and-double-buffering/

Leave a Reply