FileDocCategorySizeDatePackage
Animator.javaAPI DocExample23102Tue Dec 12 18:57:56 GMT 2000None

Animator

public class Animator extends Applet implements Runnable
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

Fields Summary
Vector
images
The images, in display order (Images).
Hashtable
durations
Duration of each image (Integers, in milliseconds).
Hashtable
sounds
Sound effects for each image (AudioClips).
Hashtable
positions
Position of each image (Points).
static final int
STARTUP_ID
MediaTracker 'class' ID numbers.
static final int
BACKGROUND_ID
static final int
ANIMATION_ID
URL
startUpImageURL
Start-up image URL, if any.
Image
startUpImage
Start-up image, if any.
URL
backgroundImageURL
Background image URL, if any.
Image
backgroundImage
Background image, if any.
URL
soundtrackURL
The soundtrack's URL.
AudioClip
soundtrack
The soundtrack.
int
maxWidth
Largest width.
int
maxHeight
Largest height.
boolean
imageLoadError
Was there a problem loading the current image?
URL
imageSource
The directory or URL from which the images are loaded
URL
soundSource
The directory or URL from which the sounds are loaded
Thread
engine
The thread animating the images.
int
frameNum
The current loop slot - index into 'images.'
Integer
frameNumKey
frameNum as an Object - suitable for use as a Hashtable key.
int
xPos
The current X position (for painting).
int
yPos
The current Y position (for painting).
public static final int
defaultPause
The default number of milliseconds to wait between frames.
int
globalPause
The global delay between images, which can be overridden by the PAUSE parameter.
boolean
userPause
Whether or not the thread has been paused by the user.
boolean
repeat
Repeat the animation? If false, just play it once.
Image
offScrImage
The offscreen image, used in double buffering
Graphics
offScrGC
The offscreen graphics context, used in double buffering
MediaTracker
tracker
The MediaTracker we use to load our images.
boolean
loaded
Can we paint yet?
boolean
error
Was there an initialization error?
static final String
imageLabel
What we call an image file in messages.
static final String
soundLabel
What we call a sound file in messages.
final boolean
debug
Print silly debugging info?
Constructors Summary
Methods Summary
voidclearLoadingMessage()

	showStatus("");
    
voiddbg(java.lang.String s)
Print silly debugging info.

	if (debug) {
	    System.out.println("> "+s);
	}
    
java.lang.StringdoSubst(java.lang.String inStr, int theInt)
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. %, for example %5 - substitute the integer left-padded with zeros to digits wide. %% - substitute a '%' here.

param
inStr the String to substitute within
param
theInt the int to substitute.

	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();
    
booleanfetchImages(java.util.Vector images)
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.

	int i;
	int size = images.size();
	for (i = 0; i < size; i++) {
	    Object o = images.elementAt(i);
	    if (o instanceof URL) {
		URL url = (URL)o;
		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((Image)images.elementAt(i)));
	}

	return true;
    
java.net.URLfetchSounds(java.util.Hashtable sounds)
Fetch the sounds named in the argument. Is restartable.

return
URL of the first bogus file we hit, null if OK.

	for (Enumeration e = sounds.keys() ; e.hasMoreElements() ;) {
	    Integer num = (Integer)e.nextElement();
	    Object o = sounds.get(num);
	    if (o instanceof URL) {
		URL file = (URL)o;
		tellLoadingMsg(file, soundLabel);
		try {
		    sounds.put(num, getAudioClip(file));
		} catch (Exception ex) {
		    return file;
		}
	    }
	}
	return null;
    
public java.lang.StringgetAppletInfo()
Applet info.


           
       
	return "Animator v1.5, by Herb Jellinek";
    
java.awt.DimensiongetImageDimensions(java.awt.Image im)
Get the dimensions of an image.

return
the image's dimensions.

	return new Dimension(im.getWidth(null), im.getHeight(null));
    
public java.lang.StringgetParameter(java.lang.String key)
Local version of getParameter for debugging purposes.

	String result = super.getParameter(key);
	dbg("getParameter("+key+") = "+result);
	return result;
    
public java.lang.String[][]getParameterInfo()
Parameter info.

	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;
    
public booleanhandleEvent(java.awt.Event evt)
Pause the thread when the user clicks the mouse in the applet. If the thread has stopped (as in a non-repeat performance), restart it.

	if (evt.id == Event.MOUSE_DOWN) {
	    if (loaded) {
		if (engine != null && engine.isAlive()) {
		    if (userPause) {
			engine.resume();
			startPlaying();
		    } else {
			engine.suspend();
			stopPlaying();
		    }
		    userPause = !userPause;
		} else {
		    userPause = false;
		    setFrameNum(0);
		    engine = new Thread(this);
		    engine.start();
		}
	    }
	    return true;
	} else {	    
	    return super.handleEvent(evt);
	}
    
public voidinit()
Initialize the applet. Get parameters.


	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 == null) ? true : (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);
    
voidloadError(java.lang.String fileName, java.lang.String fileType)

	String errorMsg = "Animator: Couldn't load "+fileType+" "+
	    fileName;
	showStatus(errorMsg);
	System.err.println(errorMsg);
	error = true;
	repaint();
    
voidloadError(java.net.URL badURL, java.lang.String fileType)

	loadError(badURL.toExternalForm(), fileType);
    
public voidpaint(java.awt.Graphics g)
Paint the current frame.

	if (error || !loaded) {
	    if (startUpImage != null) {
		if (tracker.checkID(STARTUP_ID)) {
		    g.drawImage(startUpImage, 0, 0, this);
		}
	    } else {
		if (backgroundImage != null) {
		    if (tracker.checkID(BACKGROUND_ID)) {
			g.drawImage(backgroundImage, 0, 0, this);
		    }
		} else {
		    g.clearRect(0, 0, maxWidth, maxHeight);
		}
	    }
	} else {
	    if ((images != null) && (images.size() > 0)) {
		if (frameNum < images.size()) {
		    if (backgroundImage == null) {
			offScrGC.fillRect(0, 0, maxWidth, maxHeight);
		    } else {
			offScrGC.drawImage(backgroundImage, 0, 0, this);
		    }

		    Image image = (Image)images.elementAt(frameNum);
		    Point pos = null;
		    if (positions != null) {
			pos = (Point)positions.get(frameNumKey);
		    }
		    if (pos != null) {
			xPos = pos.x;
			yPos = pos.y;
		    }
		    offScrGC.drawImage(image, xPos, yPos, this);
		    g.drawImage(offScrImage, 0, 0, this);
		} else {
		    // no more animation, but need to draw something
		    dbg("No more animation; drawing last image.");
		    if (backgroundImage == null) {
			g.fillRect(0, 0, maxWidth, maxHeight);
		    } else {
			g.drawImage(backgroundImage, 0, 0, this);
		    }
		    g.drawImage((Image)images.lastElement(), 0, 0, this);
		}
	    }
	}
    
java.util.HashtableparseDurations(java.lang.String attr, java.util.Vector images)
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 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;
    
java.util.VectorparseImages(java.lang.String attr)
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 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;
    
java.awt.PointparsePoint(java.lang.String s)
Parse a String of form xxx@yyy and return a Point.

	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)));
    
java.util.HashtableparsePositions(java.lang.String param, java.util.Vector images)
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 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;
    
java.util.HashtableparseSounds(java.lang.String attr, java.util.Vector images)
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 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;
    
java.util.VectorprepareImageRange(int startImage, int endImage, java.lang.String pattern)
Stuff a range of image names into a Vector.

return
a Vector of image URLs.

	Vector result = new Vector(Math.abs(endImage - startImage) + 1);
	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;
    
public voidrun()
Run the animation. This method is called by class Thread.

see
java.lang.Thread

	Thread me = Thread.currentThread();
	URL badURL;
	
	me.setPriority(Thread.MIN_PRIORITY);

	if (! loaded) {
	    try {
		// ... to do a bunch of loading.
		if (startUpImageURL != null) {
		    tellLoadingMsg(startUpImageURL, imageLabel);
		    startUpImage = getImage(startUpImageURL);
		    tracker.addImage(startUpImage, STARTUP_ID);
		    tracker.waitForID(STARTUP_ID);
		    if (tracker.isErrorID(STARTUP_ID)) {
			loadError(startUpImageURL, "start-up image");
		    }
		    Dimension size = getImageDimensions(startUpImage);
		    resize(size.width, size.height);
		    repaint();
		}
	    
		if (backgroundImageURL != null) {
		    tellLoadingMsg(backgroundImageURL, imageLabel);
		    backgroundImage = getImage(backgroundImageURL);
		    tracker.addImage(backgroundImage, BACKGROUND_ID);
		    tracker.waitForID(BACKGROUND_ID);
		    if (tracker.isErrorID(BACKGROUND_ID)) {
			loadError(backgroundImageURL, "background image");
		    }
		    updateMaxDims(getImageDimensions(backgroundImage));
		    repaint();
		}

		// Fetch the animation frames
		if (!fetchImages(images)) {
		    // Need to add method to MediaTracker to return
		    // files that caused errors during loading.
		    loadError("an image", imageLabel);
		    return;
		}

		if (soundtrackURL != null && soundtrack == null) {
		    tellLoadingMsg(soundtrackURL, imageLabel);
		    soundtrack = getAudioClip(soundtrackURL);
		    if (soundtrack == null) {
			loadError(soundtrackURL, "soundtrack");
			return;
		    }
		}

		if (sounds != null) {
		    badURL = fetchSounds(sounds);
		    if (badURL != null) {
			loadError(badURL, soundLabel);
			return;
		    }
		}

		clearLoadingMessage();

		offScrImage = createImage(maxWidth, maxHeight);
		offScrGC = offScrImage.getGraphics();
		offScrGC.setColor(Color.white);

		resize(maxWidth, maxHeight);
		loaded = true;
		error = false;
	    } catch (Exception e) {
		error = true;
		e.printStackTrace();
	    }
	}

	if (userPause) {
	    return;
	}

	if (repeat || frameNum < images.size()) {
	    startPlaying();
	}

	try {
	    if (images.size() > 1) {
		while (maxWidth > 0 && maxHeight > 0 && engine == me) {
		    if (frameNum >= images.size()) {
			if (!repeat) {
			    return;
			}
			setFrameNum(0);
		    }
		    repaint();

		    if (sounds != null) {
			AudioClip clip =
			    (AudioClip)sounds.get(frameNumKey);
			if (clip != null) {
			    clip.play();
			}
		    }

		    try {
			Integer pause = null;
			if (durations != null) {
			    pause = (Integer)durations.get(frameNumKey);
			}
			if (pause == null) {
			    Thread.sleep(globalPause);
			} else {
			    Thread.sleep(pause.intValue());
			}
		    } catch (InterruptedException e) {
			// Should we do anything?
		    }
		    setFrameNum(frameNum+1);
		}
	    }
	} finally {
	    stopPlaying();
	}
    
final intsetFrameNum(int newFrameNum)

	frameNumKey = new Integer(frameNum = newFrameNum);
	return frameNum;
    
voidshowParseError(java.lang.Exception e)

	String errorMsg = "Animator: Parse error: "+e;
	showStatus(errorMsg);
	System.err.println(errorMsg);
	error = true;
	repaint();
    
public voidstart()
Start the applet by forking an animation thread.

	if (engine == null) {
	    engine = new Thread(this);
	    engine.start();
	}
    
voidstartPlaying()

	if (soundtrack != null) {
	    soundtrack.loop();
	}
    
public voidstop()
Stop the insanity, um, applet.

	if (engine != null && engine.isAlive()) {
	    engine.stop();
	}
	engine = null;
    
voidstopPlaying()

	if (soundtrack != null) {
	    soundtrack.stop();
	}
    
voidtellLoadingMsg(java.lang.String file, java.lang.String fileType)

	showStatus("Animator: loading "+fileType+" "+file);
    
voidtellLoadingMsg(java.net.URL url, java.lang.String fileType)

	tellLoadingMsg(url.toExternalForm(), fileType);
    
public voidupdate(java.awt.Graphics g)
No need to clear anything; just paint.

	paint(g);
    
voidupdateMaxDims(java.awt.Dimension dim)

	maxWidth = Math.max(dim.width, maxWidth);
	maxHeight = Math.max(dim.height, maxHeight);
	dbg("New width = "+maxWidth+", height = "+maxHeight);