Animator Demo : Animation « 2D Graphics GUI « Java

Java
1. 2D Graphics GUI
2. 3D
3. Advanced Graphics
4. Ant
5. Apache Common
6. Chart
7. Class
8. Collections Data Structure
9. Data Type
10. Database SQL JDBC
11. Design Pattern
12. Development Class
13. Email
14. Event
15. File Input Output
16. Game
17. Generics
18. Hibernate
19. I18N
20. J2EE
21. J2ME
22. JDK 6
23. JSP
24. JSTL
25. Language Basics
26. Network Protocol
27. PDF RTF
28. Reflection
29. Regular Expressions
30. Scripting
31. Security
32. Servlets
33. Spring
34. Swing Components
35. Swing JFC
36. SWT JFace Eclipse
37. Threads
38. Tiny Application
39. Velocity
40. Web Services SOA
41. XML
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java » 2D Graphics GUI » AnimationScreenshots 
Animator Demo
Animator Demo

/* From http://java.sun.com/docs/books/tutorial/index.html */

/*
 * @(#)Animator.java  1.5 95/11/29 Herb Jellinek
 *
 * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted.
 * Please refer to the file http://java.sun.com/copy_trademarks.html
 * for further important copyright and trademark information and to
 * http://java.sun.com/licensing.html for further important licensing
 * information for the Java (tm) Technology.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.image.ImageProducer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * An applet that plays a sequence of images, as a loop or a one-shot. Can have
 * a soundtrack and/or sound effects tied to individual frames.
 
 @author Herb Jellinek
 @version 1.5, 29 Nov 1995
 */

public class Animator extends Applet implements Runnable {

  /**
   * The images, in display order (Images).
   */
  Vector images = null;

  /**
   * Duration of each image (Integers, in milliseconds).
   */
  Hashtable durations = null;

  /**
   * Sound effects for each image (AudioClips).
   */
  Hashtable sounds = null;

  /**
   * Position of each image (Points).
   */
  Hashtable positions = null;

  /**
   * MediaTracker 'class' ID numbers.
   */

  static final int STARTUP_ID = 0;

  static final int BACKGROUND_ID = 1;

  static final int ANIMATION_ID = 2;

  /**
   * Start-up image URL, if any.
   */
  URL startUpImageURL = null;

  /**
   * Start-up image, if any.
   */
  Image startUpImage = null;

  /**
   * Background image URL, if any.
   */
  URL backgroundImageURL = null;

  /**
   * Background image, if any.
   */
  Image backgroundImage = null;

  /**
   * The soundtrack's URL.
   */
  URL soundtrackURL = null;

  /**
   * The soundtrack.
   */
  AudioClip soundtrack;

  /**
   * Largest width.
   */
  int maxWidth = 0;

  /**
   * Largest height.
   */
  int maxHeight = 0;

  /**
   * Was there a problem loading the current image?
   */
  boolean imageLoadError = false;

  /**
   * The directory or URL from which the images are loaded
   */
  URL imageSource = null;

  /**
   * The directory or URL from which the sounds are loaded
   */
  URL soundSource = null;

  /**
   * The thread animating the images.
   */
  Thread engine = null;

  /**
   * The current loop slot - index into 'images.'
   */
  int frameNum;

  /**
   * frameNum as an Object - suitable for use as a Hashtable key.
   */
  Integer frameNumKey;

  /**
   * The current X position (for painting).
   */
  int xPos = 0;

  /**
   * The current Y position (for painting).
   */
  int yPos = 0;

  /**
   * The default number of milliseconds to wait between frames.
   */
  public static final int defaultPause = 3900;

  /**
   * The global delay between images, which can be overridden by the PAUSE
   * parameter.
   */
  int globalPause = defaultPause;

  /**
   * Whether or not the thread has been paused by the user.
   */
  boolean userPause = false;

  /**
   * Repeat the animation? If false, just play it once.
   */
  boolean repeat;

  /**
   * The offscreen image, used in double buffering
   */
  Image offScrImage;

  /**
   * The offscreen graphics context, used in double buffering
   */
  Graphics offScrGC;

  /**
   * The MediaTracker we use to load our images.
   */
  MediaTracker tracker;

  /**
   * Can we paint yet?
   */
  boolean loaded = false;

  /**
   * Was there an initialization error?
   */
  boolean error = false;

  /**
   * What we call an image file in messages.
   */
  final static String imageLabel = "image";

  /**
   * What we call a sound file in messages.
   */
  final static String soundLabel = "sound";

  /**
   * Print silly debugging info?
   */
  final boolean debug = false;

  /**
   * Applet info.
   */
  public String getAppletInfo() {
    return "Animator v1.5, by Herb Jellinek";
  }

  /**
   * Parameter info.
   */
  public String[][] getParameterInfo() {
    String[][] info = { { "imagesource""URL""a directory" },
        "startup""URL""displayed at startup" },
        "background""URL""displayed as background" },
        "startimage""int""start index" },
        "endimage""int""end index" },
        "namepattern""URL""used to generate indexed names" },
        "pause""int""milliseconds" },
        "pauses""ints""milliseconds" },
        "repeat""boolean""repeat or not" },
        "positions""coordinates""path" },
        "soundsource""URL""audio directory" },
        "soundtrack""URL""background music" },
        "sounds""URLs""audio samples" }};
    return info;
  }

  /**
   * Print silly debugging info.
   */
  void dbg(String s) {
    if (debug) {
      System.out.println("> " + s);
    }
  }

  /**
   * Local version of getParameter for debugging purposes.
   */
  public String getParameter(String key) {
    String result = super.getParameter(key);
    dbg("getParameter(" + key + ") = " + result);
    return result;
  }

  final int setFrameNum(int newFrameNum) {
    frameNumKey = new Integer(frameNum = newFrameNum);
    return frameNum;
  }

  void updateMaxDims(Dimension dim) {
    maxWidth = Math.max(dim.width, maxWidth);
    maxHeight = Math.max(dim.height, maxHeight);
    dbg("New width = " + maxWidth + ", height = " + maxHeight);
  }

  /**
   * Parse the IMAGES parameter. It looks like 1|2|3|4|5, etc., where each
   * number (item) names a source image.
   
   @return a Vector of (URL) image file names.
   */
  Vector parseImages(String attrthrows MalformedURLException {
    Vector result = new Vector(10);
    for (int i = 0; i < attr.length();) {
      int next = attr.indexOf('|', i);
      if (next == -1)
        next = attr.length();
      String file = attr.substring(i, next);
      result.addElement(new URL(imageSource, "T" + file + ".gif"));
      i = next + 1;
    }
    return result;
  }

  /**
   * Fetch the images named in the argument, updating maxWidth and maxHeight
   * as we go. Is restartable.
   
   @param images
   *            a Vector of URLs
   @return true if all went well, false otherwise.
   */
  boolean fetchImages(Vector images) {
    int i;
    int size = images.size();
    for (i = 0; i < size; i++) {
      Object o = images.elementAt(i);
      if (instanceof URL) {
        URL url = (URLo;
        tellLoadingMsg(url, imageLabel);
        Image im = getImage(url);
        tracker.addImage(im, ANIMATION_ID);
        images.setElementAt(im, i);
      }
    }

    try {
      tracker.waitForID(ANIMATION_ID);
    catch (InterruptedException e) {
    }
    if (tracker.isErrorID(ANIMATION_ID)) {
      return false;
    }

    for (i = 0; i < size; i++) {
      updateMaxDims(getImageDimensions((Imageimages.elementAt(i)));
    }

    return true;
  }

  /**
   * Parse the SOUNDS parameter. It looks like train.au||hello.au||stop.au,
   * etc., where each item refers to a source image. Empty items mean that the
   * corresponding image has no associated sound.
   
   @return a Hashtable of SoundClips keyed to Integer frame numbers.
   */
  Hashtable parseSounds(String attr, Vector images)
      throws MalformedURLException {
    Hashtable result = new Hashtable();

    int imageNum = 0;
    int numImages = images.size();
    for (int i = 0; i < attr.length();) {
      if (imageNum >= numImages)
        break;

      int next = attr.indexOf('|', i);
      if (next == -1)
        next = attr.length();

      String sound = attr.substring(i, next);
      if (sound.length() != 0) {
        result.put(new Integer(imageNum)new URL(soundSource, sound));
      }
      i = next + 1;
      imageNum++;
    }

    return result;
  }

  /**
   * Fetch the sounds named in the argument. Is restartable.
   
   @return URL of the first bogus file we hit, null if OK.
   */
  URL fetchSounds(Hashtable sounds) {
    for (Enumeration e = sounds.keys(); e.hasMoreElements();) {
      Integer num = (Integere.nextElement();
      Object o = sounds.get(num);
      if (instanceof URL) {
        URL file = (URLo;
        tellLoadingMsg(file, soundLabel);
        try {
          sounds.put(num, getAudioClip(file));
        catch (Exception ex) {
          return file;
        }
      }
    }
    return null;
  }

  /**
   * Parse the PAUSES parameter. It looks like 1000|500|||750, etc., where
   * each item corresponds to a source image. Empty items mean that the
   * corresponding image has no special duration, and should use the global
   * one.
   
   @return a Hashtable of Integer pauses keyed to Integer frame numbers.
   */
  Hashtable parseDurations(String attr, Vector images) {
    Hashtable result = new Hashtable();

    int imageNum = 0;
    int numImages = images.size();
    for (int i = 0; i < attr.length();) {
      if (imageNum >= numImages)
        break;

      int next = attr.indexOf('|', i);
      if (next == -1)
        next = attr.length();

      if (i != next - 1) {
        int duration = Integer.parseInt(attr.substring(i, next));
        result.put(new Integer(imageNum)new Integer(duration));
      else {
        result.put(new Integer(imageNum)new Integer(globalPause));
      }
      i = next + 1;
      imageNum++;
    }

    return result;
  }

  /**
   * Parse a String of form xxx@yyy and return a Point.
   */
  Point parsePoint(String sthrows ParseException {
    int atPos = s.indexOf('@');
    if (atPos == -1)
      throw new ParseException("Illegal position: " + s);
    return new Point(Integer.parseInt(s.substring(0, atPos)), Integer
        .parseInt(s.substring(atPos + 1)));
  }

  /**
   * Parse the POSITIONS parameter. It looks like 10@30|11@31|||12@20, etc.,
   * where each item is an X@Y coordinate corresponding to a source image.
   * Empty items mean that the corresponding image has the same position as
   * the preceding one.
   
   @return a Hashtable of Points keyed to Integer frame numbers.
   */
  Hashtable parsePositions(String param, Vector imagesthrows ParseException {
    Hashtable result = new Hashtable();

    int imageNum = 0;
    int numImages = images.size();
    for (int i = 0; i < param.length();) {
      if (imageNum >= numImages)
        break;

      int next = param.indexOf('|', i);
      if (next == -1)
        next = param.length();

      if (i != next) {
        result.put(new Integer(imageNum), parsePoint(param.substring(i,
            next)));
      }
      i = next + 1;
      imageNum++;
    }

    return result;
  }

  /**
   * Get the dimensions of an image.
   
   @return the image's dimensions.
   */
  Dimension getImageDimensions(Image im) {
    return new Dimension(im.getWidth(null), im.getHeight(null));
  }

  /**
   * Substitute an integer some number of times in a string, subject to
   * parameter strings embedded in the string. Parameter strings: %N -
   * substitute the integer as is, with no padding. % <digit>, for example %5 -
   * substitute the integer left-padded with zeros to <digits>digits wide. %% -
   * substitute a '%' here.
   
   @param inStr
   *            the String to substitute within
   @param theInt
   *            the int to substitute.
   */
  String doSubst(String inStr, int theInt) {
    String padStr = "0000000000";
    int length = inStr.length();
    StringBuffer result = new StringBuffer(length);

    for (int i = 0; i < length;) {
      char ch = inStr.charAt(i);
      if (ch == '%') {
        i++;
        if (i == length) {
          result.append(ch);
        else {
          ch = inStr.charAt(i);
          if (ch == 'N') {
            // just stick in the number, unmolested
            result.append(theInt + "");
            i++;
          else {
            int pad;
            if ((pad = Character.digit(ch, 10)) != -1) {
              // we've got a width value
              String numStr = theInt + "";
              String scr = padStr + numStr;
              result.append(scr.substring(scr.length() - pad));
              i++;
            else {
              result.append(ch);
              i++;
            }
          }
        }
      else {
        result.append(ch);
        i++;
      }
    }
    return result.toString();
  }

  /**
   * Stuff a range of image names into a Vector.
   
   @return a Vector of image URLs.
   */
  Vector prepareImageRange(int startImage, int endImage, String pattern)
      throws MalformedURLException {
    Vector result = new Vector(Math.abs(endImage - startImage1);
    if (pattern == null) {
      pattern = "T%N.gif";
    }
    if (startImage > endImage) {
      for (int i = startImage; i >= endImage; i--) {
        result.addElement(new URL(imageSource, doSubst(pattern, i)));
      }
    else {
      for (int i = startImage; i <= endImage; i++) {
        result.addElement(new URL(imageSource, doSubst(pattern, i)));
      }
    }
    return result;
  }

  /**
   * Initialize the applet. Get parameters.
   */
  public void init() {

    tracker = new MediaTracker(this);

    try {
      String param = getParameter("IMAGESOURCE");
      imageSource = (param == null? getDocumentBase() new URL(
          getDocumentBase(), param + "/");

      param = getParameter("PAUSE");
      globalPause = (param != null? Integer.parseInt(param)
          : defaultPause;

      param = getParameter("REPEAT");
      repeat = (param == nulltrue
          (param.equalsIgnoreCase("yes"|| param
              .equalsIgnoreCase("true"));

      int startImage = 1;
      int endImage = 1;
      param = getParameter("ENDIMAGE");
      if (param != null) {
        endImage = Integer.parseInt(param);
        param = getParameter("STARTIMAGE");
        if (param != null) {
          startImage = Integer.parseInt(param);
        }
        param = getParameter("NAMEPATTERN");
        images = prepareImageRange(startImage, endImage, param);
      else {
        param = getParameter("STARTIMAGE");
        if (param != null) {
          startImage = Integer.parseInt(param);
          param = getParameter("NAMEPATTERN");
          images = prepareImageRange(startImage, endImage, param);
        else {
          param = getParameter("IMAGES");
          if (param == null) {
            showStatus("No legal IMAGES, STARTIMAGE, or ENDIMAGE "
                "specified.");
            return;
          else {
            images = parseImages(param);
          }
        }
      }

      param = getParameter("BACKGROUND");
      if (param != null) {
        backgroundImageURL = new URL(imageSource, param);
      }

      param = getParameter("STARTUP");
      if (param != null) {
        startUpImageURL = new URL(imageSource, param);
      }

      param = getParameter("SOUNDSOURCE");
      soundSource = (param == null? imageSource : new URL(
          getDocumentBase(), param + "/");

      param = getParameter("SOUNDS");
      if (param != null) {
        sounds = parseSounds(param, images);
      }

      param = getParameter("PAUSES");
      if (param != null) {
        durations = parseDurations(param, images);
      }

      param = getParameter("POSITIONS");
      if (param != null) {
        positions = parsePositions(param, images);
      }

      param = getParameter("SOUNDTRACK");
      if (param != null) {
        soundtrackURL = new URL(soundSource, param);
      }
    catch (MalformedURLException e) {
      showParseError(e);
    catch (ParseException e) {
      showParseError(e);
    }

    setFrameNum(0);
  }

  void tellLoadingMsg(String file, String fileType) {
    showStatus("Animator: loading " + fileType + " " + file);
  }

  void tellLoadingMsg(URL url, String fileType) {
    tellLoadingMsg(url.toExternalForm(), fileType);
  }

  void clearLoadingMessage() {
    showStatus("");
  }

  void loadError(String fileName, String fileType) {
    String errorMsg = "Animator: Couldn't load " + fileType + " "
        + fileName;
    showStatus(errorMsg);
    System.err.println(errorMsg);
    error = true;
    repaint();
  }

  void loadError(URL badURL, String fileType) {
    loadError(badURL.toExternalForm(), fileType);
  }

  void showParseError(Exception e) {
    String errorMsg = "Animator: Parse error: " + e;