Animatorpublic class Animator extends Applet implements RunnableAn 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. |
Fields Summary |
---|
Vector | imagesThe images, in display order (Images). | Hashtable | durationsDuration of each image (Integers, in milliseconds). | Hashtable | soundsSound effects for each image (AudioClips). | Hashtable | positionsPosition of each image (Points). | static final int | STARTUP_IDMediaTracker 'class' ID numbers. | static final int | BACKGROUND_ID | static final int | ANIMATION_ID | URL | startUpImageURLStart-up image URL, if any. | Image | startUpImageStart-up image, if any. | URL | backgroundImageURLBackground image URL, if any. | Image | backgroundImageBackground image, if any. | URL | soundtrackURLThe soundtrack's URL. | AudioClip | soundtrackThe soundtrack. | int | maxWidthLargest width. | int | maxHeightLargest height. | boolean | imageLoadErrorWas there a problem loading the current image? | URL | imageSourceThe directory or URL from which the images are loaded | URL | soundSourceThe directory or URL from which the sounds are loaded | Thread | engineThe thread animating the images. | int | frameNumThe current loop slot - index into 'images.' | Integer | frameNumKeyframeNum as an Object - suitable for use as a Hashtable key. | int | xPosThe current X position (for painting). | int | yPosThe current Y position (for painting). | public static final int | defaultPauseThe default number of milliseconds to wait between frames. | int | globalPauseThe global delay between images, which can be overridden by
the PAUSE parameter. | boolean | userPauseWhether or not the thread has been paused by the user. | boolean | repeatRepeat the animation? If false, just play it once. | Image | offScrImageThe offscreen image, used in double buffering | Graphics | offScrGCThe offscreen graphics context, used in double buffering | MediaTracker | trackerThe MediaTracker we use to load our images. | boolean | loadedCan we paint yet? | boolean | errorWas there an initialization error? | static final String | imageLabelWhat we call an image file in messages. | static final String | soundLabelWhat we call a sound file in messages. | final boolean | debugPrint silly debugging info? |
Methods Summary |
---|
void | clearLoadingMessage()
showStatus("");
| void | dbg(java.lang.String s)Print silly debugging info.
if (debug) {
System.out.println("> "+s);
}
| java.lang.String | doSubst(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.
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();
| boolean | fetchImages(java.util.Vector images)Fetch the images named in the argument, updating
maxWidth and maxHeight as we go.
Is restartable.
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.URL | fetchSounds(java.util.Hashtable sounds)Fetch the sounds named in the argument.
Is restartable.
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.String | getAppletInfo()Applet info.
return "Animator v1.5, by Herb Jellinek";
| java.awt.Dimension | getImageDimensions(java.awt.Image im)Get the dimensions of an image.
return new Dimension(im.getWidth(null), im.getHeight(null));
| public java.lang.String | getParameter(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 boolean | handleEvent(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 void | init()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);
| void | loadError(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();
| void | loadError(java.net.URL badURL, java.lang.String fileType)
loadError(badURL.toExternalForm(), fileType);
| public void | paint(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.Hashtable | parseDurations(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.
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.Vector | parseImages(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.
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.Point | parsePoint(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.Hashtable | parsePositions(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.
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.Hashtable | parseSounds(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.
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.Vector | prepareImageRange(int startImage, int endImage, java.lang.String pattern)Stuff a range of image names into a Vector.
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 void | run()Run the animation. This method is called by class 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 int | setFrameNum(int newFrameNum)
frameNumKey = new Integer(frameNum = newFrameNum);
return frameNum;
| void | showParseError(java.lang.Exception e)
String errorMsg = "Animator: Parse error: "+e;
showStatus(errorMsg);
System.err.println(errorMsg);
error = true;
repaint();
| public void | start()Start the applet by forking an animation thread.
if (engine == null) {
engine = new Thread(this);
engine.start();
}
| void | startPlaying()
if (soundtrack != null) {
soundtrack.loop();
}
| public void | stop()Stop the insanity, um, applet.
if (engine != null && engine.isAlive()) {
engine.stop();
}
engine = null;
| void | stopPlaying()
if (soundtrack != null) {
soundtrack.stop();
}
| void | tellLoadingMsg(java.lang.String file, java.lang.String fileType)
showStatus("Animator: loading "+fileType+" "+file);
| void | tellLoadingMsg(java.net.URL url, java.lang.String fileType)
tellLoadingMsg(url.toExternalForm(), fileType);
| public void | update(java.awt.Graphics g)No need to clear anything; just paint.
paint(g);
| void | updateMaxDims(java.awt.Dimension dim)
maxWidth = Math.max(dim.width, maxWidth);
maxHeight = Math.max(dim.height, maxHeight);
dbg("New width = "+maxWidth+", height = "+maxHeight);
|
|