// Anim - stripped-down animation applet // // Based on Animator.java, but a bunch of bells and whistles have // been removed: // - no sound support // - no background image // - no startup image // - no positions // - no variable pauses // - no MediaTracker // - always repeats // // By Jef Poskanzer // // Original Animator.java by 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.*; import java.awt.*; import java.awt.image.*; import java.util.*; import java.net.*; // An applet that plays a sequence of images as a loop. public class Anim extends Applet implements Runnable { // The directory or URL from which the images are loaded. URL imageSource = null; // How many images. int frameCount = 0; // The image URLs. URL[] imageURLs; // The images. Image[] images; // All images combined into one. Image combinedImage = null; // The thread animating the images. Thread thread = null; // 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; // The current loop slot - index into 'images.' int frameNum; /// Applet info. public String getAppletInfo() { return getClass().getName() + ", by Jef Poskanzer - stripped-down version of Animator v1.5, by Herb Jellinek"; } // Parameter info. public String[][] getParameterInfo() { String[][] info = { { "pause", "int", "milliseconds between frames" }, { "imagesource", "URL", "directory where images live" }, { "startimage", "int", "first index" }, { "endimage", "int", "last index" }, { "namepattern", "URL", "used to generate indexed names" }, { "combinedimage", "URL", "all image frames combined into one" }, { "combinedcount", "int", "how many frames in the combined image" }, }; return info; } // Fetch the images. void fetchImages() { images = new Image[frameCount]; for ( int i = 0; i < frameCount; ++i ) images[i] = getImage( imageURLs[i] ); } // 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 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 = Character.digit( ch, 10 ); if ( pad != -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(); } // Create a range of image URLs. void prepareImageURLs( int startImage, int endImage, String pattern ) throws MalformedURLException { frameCount = Math.abs( endImage - startImage ) + 1; imageURLs = new URL[frameCount]; if ( pattern == null ) pattern = "T%N.gif"; int j = 0; if ( startImage > endImage ) { for ( int i = startImage; i >= endImage; --i ) imageURLs[j++] = new URL( imageSource, doSubst( pattern, i ) ); } else { for ( int i = startImage; i <= endImage; ++i) imageURLs[j++] = new URL( imageSource, doSubst( pattern, i ) ); } } // Initialize the applet. Get parameters. public void init() { Acme.GuiUtils.handleBgcolor( this ); try { String param = getParameter( "imagesource" ); imageSource = ( param == null ) ? getDocumentBase() : new URL( getDocumentBase(), param + "/" ); param = getParameter( "pause" ); globalPause = ( param == null ) ? defaultPause : Integer.parseInt( param ); int startImage = 1; int endImage = 1; param = getParameter( "startimage" ); if ( param != null ) startImage = Integer.parseInt( param ); param = getParameter( "endimage" ); if ( param != null ) endImage = Integer.parseInt( param ); param = getParameter( "combinedimages" ); if ( param != null ) { URL url = new URL( imageSource, param ); combinedImage = getImage( url ); param = getParameter( "combinedcount" ); if ( param != null ) frameCount = Integer.parseInt( param ); else showError( "combinedcount required with combinedimages" ); } else { param = getParameter( "namepattern" ); prepareImageURLs( startImage, endImage, param ); fetchImages(); } } catch ( Exception e ) { showError( "Anim initialization error: " + e ); } frameNum = 0; } void showError( String errorMsg ) { showStatus( errorMsg ); System.err.println( errorMsg ); } /// Start the applet by forking an animation thread. public void start() { if ( thread == null ) { thread = new Thread( this ); thread.start(); } } /// Called when the applet should stop itself. public void stop() { if ( thread != null ) { // Stop the thread. if ( thread.isAlive() ) thread.stop(); thread = null; } } /// Event handler. public boolean handleEvent( Event event ) { if ( event.id == Event.MOUSE_UP ) { // Toggle the run state. if ( thread == null ) start(); else stop(); return true; } return super.handleEvent( event ); } /// Run the animation. This method is called by class Thread. public synchronized void run() { Thread me = Thread.currentThread(); me.setPriority( Thread.MIN_PRIORITY ); if ( frameCount >= 1 ) { while ( thread == me ) { if ( frameNum >= frameCount ) frameNum = 0; repaint(); try { wait( globalPause ); } catch ( InterruptedException e ) {} ++frameNum; } } } // The default update() clears the background and then calls paint(). // This one doesn't clear the background. public void update( Graphics graphics ) { paint( graphics ); } Image offScrImage = null; int offScrWidth = 0; int offScrHeight = 0; Graphics offScrGC; // Called when the applet should paint itself. public synchronized void paint( Graphics graphics ) { if ( combinedImage != null ) paintCombined( graphics ); else if ( images != null ) paintSeparate( graphics ); } // Paint routine for combined images. public void paintCombined( Graphics graphics ) { int width = combinedImage.getWidth( this ); int height = combinedImage.getHeight( this ); if ( width == -1 || height == -1 ) { brokenImage( graphics, this ); return; } width /= frameCount; checkOffScr( width, height ); offScrGC.fillRect( 0, 0, offScrWidth, offScrHeight ); offScrGC.drawImage( combinedImage, -frameNum * width, 0, this ); graphics.drawImage( offScrImage, 0, 0, this ); } // Paint routine for separate images. public void paintSeparate( Graphics graphics ) { Image img = images[frameNum]; if ( img == null ) { brokenImage( graphics, this ); return; } int width = img.getWidth( this ); int height = img.getHeight( this ); if ( width == -1 || height == -1 ) { brokenImage( graphics, this ); return; } checkOffScr( width, height ); offScrGC.fillRect( 0, 0, offScrWidth, offScrHeight ); offScrGC.drawImage( img, 0, 0, this ); graphics.drawImage( offScrImage, 0, 0, this ); } int brokenCount = 0; void brokenImage( Graphics graphics, Component comp ) { ++brokenCount; if ( brokenCount > 10 ) Acme.GuiUtils.drawImage( graphics, comp, Acme.GuiUtils.brokenIcon( comp ) ); } // Make sure we have an off-screen image and it's big enough. void checkOffScr( int width, int height ) { if ( offScrImage == null || width > offScrWidth || height > offScrHeight ) { if ( width > offScrWidth ) offScrWidth = width; if ( height > offScrHeight ) offScrHeight = height; offScrImage = createImage( offScrWidth, offScrHeight ); offScrGC = offScrImage.getGraphics(); offScrGC.setColor( getBackground() ); } } }