Java Forum / GUI / November 2003
Need some help with animation
Rhino - 25 Nov 2003 22:00 GMT I need some help with animation.
Specifically, I am trying to create a class that will play an animated GIF (GIF89a) in a JPanel and stop or start when the user clicks the mouse. This seems like something that should be pretty straight forward but I've already put several hours into this without getting the results I want.
My class *almost* works satisfactorily. The animation starts and stops when I click (actually, I do two clicks to start it the very first time, after that the mouse toggles the animation with each click.) Unfortunately, the animation also disappears whenever it is not running. That's the part I need help with.
I would like the animated GIF to be visible and initially stopped on the first frame when it is started the first time, then start when I've clicked the mouse (twice if it's the very first time since the panel was instantiated). The animation should stop but leave its current frame visible when I click the mouse again, then restart from where it left off on the next click.
I've tried everything I can think of and researched the issue as thoroughly as I could in Usenet but I can't find an example that does this or any discussion of the relevant principles so that I can figure it out for myself.
Here is my code with most of the comments removed so that the post is not overly long. Please feel free to tell me to do things differently. Personally, I find the logic in the paintComponent() method a little odd-looking but I can't think of anything else to try there that hasn't already failed to work.
package racer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AnimationPanel extends JPanel implements Runnable {
final String CLASS_NAME = getClass().getName();
static final boolean DEBUG = false;
Thread animationThread = null;
boolean keepDrawing;
boolean frozen = false;
Image animation = null;
MediaTracker tracker = null;
int stillWidth = 0;
int stillHeight = 0;
public static void main(String[] args) {
JFrame myFrame = new JFrame("Drawing Panel");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
AnimationPanel drawingPanel = new AnimationPanel("racer/AnimatedGIFs", "catwalk.gif", true);
myFrame.getContentPane().add(drawingPanel, BorderLayout.CENTER);
myFrame.pack();
myFrame.setVisible(true);
}
public AnimationPanel(String animationImageFilePath, String animationImageFile, boolean controlWithMouse) {
super();
/* Get the URL for the animated image file. */
URL animationGIF = this.getClass().getClassLoader().getResource(animationImageFilePath + File.separator + animationImageFile);
/* Get the actual image file. */
animation = Toolkit.getDefaultToolkit().getImage(animationGIF);
try {
tracker = new MediaTracker(this);
tracker.addImage(animation, 0);
tracker.waitForAll();
}
catch (Exception excp) {
excp.printStackTrace();
}
/* Assuming that all of the stills in the animation are the same size, determine the
height and width of the panel that will be needed to display them. */
stillWidth = animation.getWidth(this);
stillHeight = animation.getHeight(this);
/* If the user wishes to control the animation with the mouse, activate this listener. */
if (controlWithMouse) {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
if (frozen) {
frozen = false;
startAnimation();
}
else {
frozen = true;
stopAnimation();
}
}
});
} //end if
} //end AnimationPanel()
public void startAnimation() {
if (animationThread == null) {
animationThread = new Thread(this);
keepDrawing = true;
animationThread.start();
}
} //end startAnim()
public void stopAnimation() {
if (animationThread != null) {
keepDrawing = false;
animationThread = null;
}
} //end stopAnim()
public void run() {
while (keepDrawing) {
repaint(); //call paint
try {
Thread.sleep(10); //sleep a little to slow it down
}
catch (InterruptedException ie_excp) {
System.err.println("Error: " + ie_excp);
}
} //end while
} // end run()
public void paintComponent(Graphics graphics) {
if (!keepDrawing) {
/* If we DON'T paint the parent class (super), other parts of the GUI which imbed this
* panel overwrite the area where the animation is drawn. If we DO paint the parent
* class, the animation is blanked out so it disappears when the animation is stopped
* rather than freezing on the last frame.
*/
super.paintComponent(graphics);
return;
}
else {
super.paintComponent(graphics);
graphics.drawImage(animation, 0, 0, this);
}
} //end paint()
public Dimension getPreferredSize() {
return new Dimension(stillWidth, stillHeight);
}
} //end of class
 Signature Rhino --- rhino1 AT sympatico DOT ca "If you want the best seat in the house, you'll have to move the cat."
ak - 26 Nov 2003 11:33 GMT You have some mistakes here.
a) you load only FIRST frame from your GIF, try to use ImageIO to get all of them b) use MemoryImageSource to create animation. See javadoc for details.
hope it helps
> I need some help with animation. > [quoted text clipped - 258 lines] > rhino1 AT sympatico DOT ca > "If you want the best seat in the house, you'll have to move the cat." Rhino - 26 Nov 2003 13:51 GMT > You have some mistakes here. > > a) you load only FIRST frame from your GIF, try to use ImageIO to get all of > them Not really. You are right that I am loading only only file but that file is an animated GIF (GIF89a) so it contains all the frames.
> b) use MemoryImageSource to create animation. See javadoc for details. I'll have a look at this.
Thanks!
Rhino
> > I need some help with animation. > > [quoted text clipped - 269 lines] > > rhino1 AT sympatico DOT ca > > "If you want the best seat in the house, you'll have to move the cat." ak - 26 Nov 2003 18:54 GMT > > You have some mistakes here. > > [quoted text clipped - 4 lines] > Not really. You are right that I am loading only only file but that file is > an animated GIF (GIF89a) so it contains all the frames. Yes, file contains all frames, but java picks only the first of them.
> > b) use MemoryImageSource to create animation. See javadoc for details. > > [quoted text clipped - 283 lines] > > > rhino1 AT sympatico DOT ca > > > "If you want the best seat in the house, you'll have to move the cat." Rhino - 27 Nov 2003 13:32 GMT > > "ak" <k.andrei@gmx.de.spam> wrote in message > news:bq2364$12j$1@online.de... [quoted text clipped - 10 lines] > > Yes, file contains all frames, but java picks only the first of them. Try running my code with any animated GIF of your choice. You'll see that it is playing all the frames in the file.
> > > b) use MemoryImageSource to create animation. See javadoc for details. > > > [quoted text clipped - 289 lines] > > > > rhino1 AT sympatico DOT ca > > > > "If you want the best seat in the house, you'll have to move the cat." zn?rt - 27 Nov 2003 19:29 GMT >My class *almost* works satisfactorily. The animation starts and stops when >I click (actually, I do two clicks to start it the very first time, after Not really. You are not doing the animation, java is. You are just displaying an animated GIF image. I think you can't achieve your goal if you don't control the animation yourself.
You would realize this if you fix your paintComponent method, which is worng, as for what you want. You can't simply "skip" drawing of the animation when you are not in "keepDrawing" state. That's why yur animation dissapears, it gets painted over. Instead, you should always draw the current frame. And in your code you have no idea which this frame this should be. As "ak" tolds you, you need to have al frames "separately" in memory. But I'll bet there are view componentss with "pause/resume" behaviours out there.
regards znôrt
ak - 27 Nov 2003 20:57 GMT /* * @(#)Animator.java 1.5 99/07/12 * * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes. */
import java.awt.*; import java.awt.event.*; import java.applet.Applet; import java.applet.AudioClip; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.net.URL; import java.net.MalformedURLException; import java.util.List; import java.util.ArrayList; import java.util.Iterator;
/** * 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.
* See the <a href="http://java.sun.com/applets/applets/Animator/">Animator * home page</a> for details and updates. * * @author Herb Jellinek * @version 1.5, 07/12/99 */ public class Animator extends Applet implements Runnable, MouseListener { int appWidth = 0; // Animator width int appHeight = 0; // Animator height Thread engine = null; // Thread animating the images boolean userPause = false; // True if thread currently paused by user
boolean loaded = false; // Can we paint yet? boolean error = false; // Was there an initialization error? Animation animation = null; // Animation this animator contains String hrefTarget = null; // Frame target of reference URL if any URL hrefURL = null; // URL link for information if any
static final String sourceLocation = "http://java.sun.com/applets/applets/Animator/";
static final String userInstructions = "shift-click for errors, info"; static final int STARTUP_ID = 0; static final int BACKGROUND_ID = 1; static final int ANIMATION_ID = 2;
/** * Applet info. */ public String getAppletInfo() { return "Animator v1.10 (02/05/97), by Herb Jellinek"; }
/** * Parameter info. */ public String[][] getParameterInfo() { String[][] info = { {"imagesource", "URL", "a directory"},
{"startup", "URL", "image displayed at start-up"}, {"backgroundcolor", "int", "background color (24-bit RGB number)"}, {"background", "URL", "image displayed as background"}, {"startimage", "int", "index of first image"}, {"endimage", "int", "index of last image"}, {"namepattern", "URL", "generates indexed names"},
{"images", "URLs", "list of image indices"}, {"href", "URL", "page to visit on mouse-click"}, {"target", "name", "frame to put that page in"}, {"pause", "int", "global pause, milliseconds"}, {"pauses", "ints", "individual pauses, milliseconds"}, {"repeat", "boolean", "repeat? true or false"},
{"positions", "coordinates", "path images will follow"}, {"soundsource", "URL", "audio directory"}, {"soundtrack", "URL", "background music"}, {"sounds", "URLs", "list of audio samples"}, }; return info; }
/** * Show a crude "About" box. Displays credits, errors (if any), and * parameter values and documentation.
*/ void showDescription() { DescriptionFrame description = new DescriptionFrame(); description.tell("\t\t"+getAppletInfo()+"\n"); description.tell("Updates, documentation at "+sourceLocation+"\n\n"); description.tell("Document base: "+getDocumentBase()+"\n"); description.tell("Code base: "+getCodeBase()+"\n\n");
Object errors[] = animation.tracker.getErrorsAny(); if (errors != null) {
description.tell("Applet image errors:\n"); for (int i = 0; i < errors.length; i++) { if (errors[i] instanceof Image) { AnimationFrame frame = (AnimationFrame) animation.frames.get(i); URL url = frame.imageLocation; if (url != null) { description.tell(" "+url+" not loaded\n"); }
} } description.tell("\n"); } if (animation.frames == null || animation.frames.size() == 0) description.tell("\n** No images loaded **\n\n"); description.tell("Applet parameters:\n"); description.tell(" width = "+getParameter("WIDTH")+"\n"); description.tell(" height = "+getParameter("HEIGHT")+"\n"); String params[][] = getParameterInfo(); for (int i = 0; i < params.length; i++) {
String name = params[i][0]; description.tell(" "+name+" = "+getParameter(name)+ "\t ["+params[i][2]+"]\n"); } description.show(); }
/** * Local version of getParameter for debugging purposes. */ public String getParam(String key) { String result = getParameter(key); return result; }
/** * Get parameters and parse them */ public void handleParams() {
try { String param = getParam("IMAGESOURCE"); animation.imageSource = (param == null) ? getDocumentBase() : new URL(getDocumentBase(), param + "/");
String href = getParam("HREF"); if (href != null) { try { hrefURL = new URL(getDocumentBase(), href); } catch (MalformedURLException e) { showParseError(e); }
}
hrefTarget = getParam("TARGET"); if (hrefTarget == null) hrefTarget = "_top"; param = getParam("PAUSE"); if (param != null) animation.setGlobalPause(Integer.parseInt(param)); param = getParam("REPEAT"); animation.repeat = (param == null) ? true : (param.equalsIgnoreCase("yes") || param.equalsIgnoreCase("true"));
int startImage = 1; int endImage = 1; param = getParam("ENDIMAGE"); if (param != null) { endImage = Integer.parseInt(param); param = getParam("STARTIMAGE"); if (param != null) { startImage = Integer.parseInt(param); } param = getParam("NAMEPATTERN"); animation.prepareImageRange(startImage, endImage, param);
} else { param = getParam("STARTIMAGE"); if (param != null) { startImage = Integer.parseInt(param); param = getParam("NAMEPATTERN"); animation.prepareImageRange(startImage, endImage, param); } else { param = getParam("IMAGES"); if (param == null) { showStatus("No legal IMAGES, STARTIMAGE, or ENDIMAGE "+
"specified."); error = true; return; } else { animation.parseImages(param, getParam("NAMEPATTERN")); } } }
param = getParam("BACKGROUND"); if (param != null) animation.backgroundImageURL = new URL(animation.imageSource, param);
param = getParam("BACKGROUNDCOLOR"); if (param != null) animation.backgroundColor = decodeColor(param); param = getParam("STARTUP"); if (param != null) animation.startUpImageURL = new URL(animation.imageSource, param); param = getParam("SOUNDSOURCE"); animation.soundSource = (param == null) ? animation.imageSource
new URL(getDocumentBase(), param + "/"); param = getParam("SOUNDS"); if (param != null) animation.parseSounds(param); param = getParam("PAUSES"); if (param != null) animation.parseDurations(param); param = getParam("POSITIONS"); if (param != null) animation.parsePositions(param); param = getParam("SOUNDTRACK");
if (param != null) animation.soundTrackURL = new URL( animation.soundSource, param); } catch (MalformedURLException e) { showParseError(e); } catch (ParseException e) { showParseError(e); } }
private Color decodeColor(String s) { int val = 0; try { if (s.startsWith("0x")) { val = Integer.parseInt(s.substring(2), 16);
} else if (s.startsWith("#")) { val = Integer.parseInt(s.substring(1), 16); } else if (s.startsWith("0") && s.length() > 1) { val = Integer.parseInt(s.substring(1), 8); } else { val = Integer.parseInt(s, 10); } return new Color(val); } catch (NumberFormatException e) { return null; } }
/** * Initialize the applet. Get parameters.
*/ public void init() {
//animation.tracker = new MediaTracker(this); appWidth = getSize().width; appHeight = getSize().height; animation = new Animation(this); handleParams(); animation.init(); addMouseListener(this); Thread me = Thread.currentThread(); me.setPriority(Thread.MIN_PRIORITY); userPause = false; }
public void destroy() { removeMouseListener(this);
}
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; showStatus(errorMsg); System.err.println(errorMsg); error = true; repaint(); }
/** * Run the animation. This method is called by class Thread.
* @see java.lang.Thread */ public void run() { Thread me = Thread.currentThread(); if (animation.frames == null) return; if ((appWidth <= 0) || (appHeight <= 0)) return; try { while (engine == me) { // Get current frame and paint it, play its sound AnimationFrame thisFrame = (AnimationFrame) animation.frames.get(animation.currentFrame);
repaint(); if (thisFrame.sound != null) thisFrame.sound.play();
animation.currentFrame++; // Check if we are done if (animation.currentFrame >= animation.frames.size()) { if (animation.repeat) animation.currentFrame = 0; else return; }
// Pause for duration or longer if user paused
try { Thread.sleep(thisFrame.duration); synchronized(this) { while (userPause) { animation.stopPlaying(); wait(); } } } catch (InterruptedException e) { } } } finally { synchronized(this) { if (engine == me)
animation.stopPlaying(); } } }
/** * No need to clear anything; just paint. */ public void update(Graphics g) { paint(g); }
/** * Paint the current frame */ public void paint(Graphics g) { if (error || ! loaded) { if (animation.startUpImage != null) { if (animation.tracker.checkID(STARTUP_ID)) { if (animation.backgroundColor != null) {
g.setColor(animation.backgroundColor); g.fillRect(0, 0, appWidth, appHeight); } g.drawImage(animation.startUpImage, 0, 0, this); } } else { if ((animation.backgroundImage != null) && (animation.tracker.checkID(BACKGROUND_ID))) g.drawImage(animation.backgroundImage, 0, 0, this); else
g.clearRect(0, 0, appWidth, appHeight); } } else { animation.paint(g); } }
/** * Start the applet by forking an animation thread. */ public void start() { engine = new Thread(this); engine.start(); showStatus(getAppletInfo()); }
/** * Stop the insanity, um, applet. */ public synchronized void stop() { engine = null;
animation.stopPlaying(); if (userPause) { userPause = false; notify(); } }
/** * 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. */ public synchronized void mousePressed(MouseEvent event) { event.consume(); if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) { showDescription();
return; } else if (hrefURL != null) { //Let mouseClicked handle this. return; } else if (loaded) { userPause = !userPause; if (!userPause) { animation.startPlaying(); notifyAll(); } } }
public void mouseClicked(MouseEvent event) { if ((hrefURL != null) && ((event.getModifiers() & InputEvent.SHIFT_MASK) == 0)) {
getAppletContext().showDocument(hrefURL, hrefTarget); } }
public void mouseReleased(MouseEvent event) { }
public void mouseEntered(MouseEvent event) { showStatus(getAppletInfo()+" -- "+userInstructions); }
public void mouseExited(MouseEvent event) { showStatus(""); } }
/** * A class that represents an animation to be displayed by the applet */ class Animation extends Object { static final int STARTUP_ID = 0;
static final int BACKGROUND_ID = 1; static final int ANIMATION_ID = 2; static final String imageLabel = "image"; static final String soundLabel = "sound";
int globalPause = 1300; // global pause in milleseconds List frames = null; // List holding frames of animation int currentFrame; // Index into images for current position Image startUpImage = null; // The startup image if any Image backgroundImage = null; // The background image if any
AudioClip soundTrack = null; // The soundtrack for this animation Color backgroundColor = null; // Background color if any URL backgroundImageURL = null; // URL of background image if any URL startUpImageURL = null; // URL of startup image if any URL soundTrackURL = null; // URL of soundtrack URL imageSource = null; // Directory or URL for images URL soundSource = null; // Directory or URL for sounds boolean repeat; // Repeat the animation if true
Image offScrImage; // Offscreen image Graphics offScrGC; // Offscreen graphics context MediaTracker tracker; // MediaTracker used to load images Animator owner; // Applet that contains this animation
Animation(Animator container) { super(); owner = container; }
void init() { tracker = new MediaTracker(owner); currentFrame = 0; loadAnimationMedia();
startPlaying(); }
void setGlobalPause(int pause) { globalPause = pause; }
/** * Loads the images and sounds involved with this animation */ void loadAnimationMedia() { URL badURL; boolean error; try { if (startUpImageURL != null) { owner.tellLoadingMsg(startUpImageURL, imageLabel); startUpImage = fetchImageAndWait(startUpImageURL, STARTUP_ID);
if (tracker.isErrorID(STARTUP_ID)) { owner.loadError(startUpImageURL, "start-up image"); } owner.repaint(); }
if (backgroundImageURL != null) { owner.tellLoadingMsg(backgroundImageURL, imageLabel); backgroundImage = fetchImageAndWait(backgroundImageURL, BACKGROUND_ID); if (tracker.isErrorID(BACKGROUND_ID))
owner.loadError(backgroundImageURL, "background image"); owner.repaint(); }
// Fetch the animation frame images Iterator iterator = frames.iterator(); while(iterator.hasNext()) { AnimationFrame frame = (AnimationFrame) iterator.next(); owner.tellLoadingMsg(frame.imageLocation, imageLabel); frame.image = owner.getImage(frame.imageLocation);
tracker.addImage(frame.image, ANIMATION_ID); try { tracker.waitForID(ANIMATION_ID); } catch (InterruptedException e) {} }
if (soundTrackURL != null && soundTrack == null) { owner.tellLoadingMsg(soundTrackURL, imageLabel); soundTrack = owner.getAudioClip(soundTrackURL); if (soundTrack == null) { owner.loadError(soundTrackURL, "soundtrack");
return; } }
// Load the sounds into their frames iterator = frames.iterator(); while(iterator.hasNext()) { AnimationFrame frame = (AnimationFrame) iterator.next(); if (frame.soundLocation != null) { owner.tellLoadingMsg(frame.soundLocation, soundLabel); try { frame.sound = owner.getAudioClip(frame.soundLocation);
} catch (Exception ex) { owner.loadError(frame.soundLocation, soundLabel); } } }
owner.clearLoadingMessage(); offScrImage = owner.createImage(owner.appWidth, owner.appHeight); offScrGC = offScrImage.getGraphics(); offScrGC.setColor(Color.lightGray); owner.loaded = true; error = false; } catch (Exception e) {
error = true; e.printStackTrace(); } }
/** * Fetch an image and wait for it to come in. Used to enforce a load * order for background and startup images. */ Image fetchImageAndWait(URL imageURL, int trackerClass) throws InterruptedException { Image image = owner.getImage(imageURL); tracker.addImage(image, trackerClass); tracker.waitForID(trackerClass);
return image; }
/** * Stuff a range of image names into a List * @return a List of image URLs. */ void prepareImageRange(int startImage, int endImage, String pattern) throws MalformedURLException { frames = new ArrayList(Math.abs(endImage - startImage) + 1); if (pattern == null) pattern = "T%N.gif"; if (startImage > endImage) { for (int i = startImage; i >= endImage; i--) {
AnimationFrame frame = new AnimationFrame(); frames.add(frame); frame.duration = globalPause; frame.imageLocation = new URL(imageSource, doSubst(pattern, i+"")); } } else { for (int i = startImage; i <= endImage; i++) { AnimationFrame frame = new AnimationFrame(); frames.add(frame); frame.duration = globalPause;
frame.imageLocation = new URL(imageSource, doSubst(pattern, i+"")); } } }
/** * 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. */ void parseSounds(String attr) throws MalformedURLException { int frameIndex = 0;
int numFrames = frames.size(); for (int i = 0; (i < attr.length()) && (frameIndex < numFrames); ) { int next = attr.indexOf('|', i); if (next == -1) next = attr.length(); String sound = attr.substring(i, next); if (sound.length() != 0) { AnimationFrame frame = (AnimationFrame) frames.get(frameIndex); frame.soundLocation = new URL(soundSource, sound); } i = next + 1;
frameIndex++; } }
/** * Parse the IMAGES parameter. It looks like * 1|2|3|4|5, etc., where each number (item) names a source image. */ void parseImages(String attr, String pattern) throws MalformedURLException { frames = new ArrayList(); if (pattern == null) pattern = "T%N.gif"; for (int i = 0; i < attr.length(); ) { int next = attr.indexOf('|', i);
if (next == -1) next = attr.length(); String file = attr.substring(i, next); AnimationFrame frame = new AnimationFrame(); frames.add(frame); frame.imageLocation = new URL(imageSource, doSubst(pattern, file)); frame.duration = globalPause; i = next + 1; } }
/** * 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. */ void parseDurations(String attr) { int imageNum = 0; int numImages = frames.size(); for (int i = 0; (i < attr.length()) && (imageNum < numImages); ) { int next = attr.indexOf('|', i); if (next == -1) next = attr.length();
AnimationFrame aFrame = (AnimationFrame) frames.get(imageNum); if (i != next) { int duration = Integer.parseInt(attr.substring(i, next)); aFrame.duration = duration; } i = next + 1; imageNum++; } }
/** * Parse a String of form xxx@yyy and return a Point. */ Point parsePoint(String s) throws 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. */ void parsePositions(String param) throws ParseException { int imageNum = 0; int numImages = frames.size(); for (int i = 0; (i < param.length()) && (imageNum < numImages); ) { int next = param.indexOf('|', i); if (next == -1) next = param.length(); if (i != next) { AnimationFrame frame = (AnimationFrame) frames.get(imageNum);
frame.position = parsePoint(param.substring(i, next)); } i = next + 1; imageNum++; } }
/** * 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, as a String. */ String doSubst(String inStr, String 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' || 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(); }
void startPlaying() { if (soundTrack != null) soundTrack.loop(); }
void stopPlaying() { if (soundTrack != null) soundTrack.stop(); }
public void paint(Graphics g) { int xPos = 0; int yPos = 0; if ((frames.size() > 0) && tracker.checkID(ANIMATION_ID) && (offScrGC != null)) {
AnimationFrame frame = (AnimationFrame) frames.get(currentFrame); Image image = frame.image; if (backgroundImage == null) { offScrGC.clearRect(0, 0, owner.appWidth, owner.appHeight); } else offScrGC.drawImage(backgroundImage, 0, 0, owner); Point pos = null; if (frame.position != null) { xPos = frame.position.x; yPos = frame.position.y;
} if (backgroundColor != null) { offScrGC.setColor(backgroundColor); offScrGC.fillRect(0, 0, owner.appWidth, owner.appHeight); offScrGC.drawImage(image, xPos, yPos, backgroundColor, owner); } else { offScrGC.drawImage(image, xPos, yPos, owner); } if (offScrImage != null) g.drawImage(offScrImage, 0, 0, owner); } }
}
/** * Instances of this class represent a single frame of an animation * There can be an image, sound, and position associated with each frame */ class AnimationFrame extends Object { static final String imageLabel = "image"; static final String soundLabel = "sound";
URL imageLocation = null; // Directory or URL of this frames image URL soundLocation = null; // Directory or URL of this frames sound int duration; // Duration time for this frame in milliseconds
AudioClip sound; // Sound associated with this frame object Image image; // Image associated with this frame Point position; // Position of this frame
}
/** * ParseException: signals a parameter parsing problem. */ class ParseException extends Exception { ParseException(String s) { super(s); } }
/** * DescriptionFrame: implements a pop-up "About" box. */ class DescriptionFrame extends Frame implements ActionListener {
static final int rows = 27; static final int cols = 70; TextArea info; Button cancel;
DescriptionFrame() { super("Animator v1.10"); add("Center", info = new TextArea(rows, cols)); info.setEditable(false); info.setBackground(Color.white); Panel buttons = new Panel(); add("South", buttons); buttons.add(cancel = new Button("Cancel")); cancel.addActionListener(this); pack();
}
public void show() { info.select(0,0); super.show(); }
void tell(String s) { info.append(s); }
public void actionPerformed(ActionEvent e) { setVisible(false); } }
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|