home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)MediaTracker.java 1.6 95/10/08 Jim Graham
- *
- * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
- *
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for NON-COMMERCIAL purposes and without
- * fee is hereby granted provided that this copyright notice
- * appears in all copies. Please refer to the file "copyright.html"
- * for further important copyright and licensing information.
- *
- * 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.
- */
-
- package java.awt;
-
- import java.awt.Component;
- import java.awt.Image;
- import java.awt.Graphics;
- import java.awt.image.ImageObserver;
-
- /**
- * A utility class to track the status of a number of media objects.
- * Media objects could include images as well as audio clips, though
- * currently only images are supported. To use it, simply create an
- * instance and then call addImage() for each image to be tracked.
- * Each image can be assigned a unique ID for indentification purposes.
- * The IDs control the priority order in which the images are fetched
- * as well as identifying unique subsets of the images that can be
- * waited on independently. Here is an example:
- * <pre>
- *
- * import java.applet.Applet;
- * import java.awt.Color;
- * import java.awt.Image;
- * import java.awt.Graphics;
- * import java.awt.MediaTracker;
- *
- * public class ImageBlaster extends Applet implements Runnable {
- * MediaTracker tracker;
- * Image bg;
- * Image anim[] = new Image[5];
- * int index;
- * Thread animator;
- *
- * // Get the images for the background (id == 0) and the animation
- * // frames (id == 1) and add them to the MediaTracker
- * public void init() {
- * tracker = new MediaTracker(this);
- * bg = getImage(getDocumentBase(), "images/background.gif");
- * tracker.addImage(bg, 0);
- * for (int i = 0; i < 5; i++) {
- * anim[i] = getImage(getDocumentBase(), "images/anim"+i+".gif");
- * tracker.addImage(anim[i], 1);
- * }
- * }
- * // Start the animation thread.
- * public void start() {
- * animator = new Thread(this);
- * animator.start();
- * }
- * // Stop the animation thread.
- * public void stop() {
- * animator.stop();
- * animator = null;
- * }
- * // Run the animation thread.
- * // First wait for the background image to fully load and paint.
- * // Then wait for all of the animation frames to finish loading.
- * // Finally loop and increment the animation frame index.
- * public void run() {
- * try {
- * tracker.waitForID(0);
- * tracker.waitForID(1);
- * } catch (InterruptedException e) {
- * return;
- * }
- * Thread me = Thread.currentThread();
- * while (animator == me) {
- * try {
- * Thread.sleep(100);
- * } catch (InterruptedException e) {
- * break;
- * }
- * synchronized (this) {
- * index++;
- * if (index >= anim.length) {
- * index = 0;
- * }
- * }
- * repaint();
- * }
- * }
- * // The background image fills our frame so we don't need to clear
- * // the applet on repaints, just call the paint method.
- * public void update(Graphics g) {
- * paint(g);
- * }
- * // Paint a large red rectangle if there are any errors loading the
- * // images. Otherwise always paint the background so that it appears
- * // incrementally as it is loading. Finally, only paint the current
- * // animation frame if all of the frames (id == 1) are done loading
- * // so that we don't get partial animations.
- * public void paint(Graphics g) {
- * if (tracker.isErrorAny()) {
- * g.setColor(Color.red);
- * g.fillRect(0, 0, size().width, size().height);
- * return;
- * }
- * g.drawImage(bg, 0, 0, this);
- * if (tracker.checkID(1)) {
- * g.drawImage(anim[index], 10, 10, this);
- * }
- * }
- * }
- *
- * </pre>
- *
- * @version 1.6, 10/08/95
- * @author Jim Graham
- */
- public class MediaTracker {
- Component target;
- MediaEntry head;
-
- /**
- * Create a Media tracker to track images for a given Component.
- * @param comp the component on which the images will eventually be drawn
- */
- public MediaTracker(Component comp) {
- target = comp;
- }
-
- /**
- * Add an image to the list of images being tracked. The image
- * will eventually be rendered at its default (unscaled) size.
- * @param image the image to be tracked
- * @param id the identifier used to later track this image
- */
- public void addImage(Image image, int id) {
- addImage(image, id, -1, -1);
- }
-
- /**
- * Add a scaled image to the list of images being tracked. The
- * image will eventually be rendered at the indicated size.
- * @param image the image to be tracked
- * @param id the identifier used to later track this image
- * @param w the width that the image will be rendered at
- * @param h the height that the image will be rendered at
- */
- public synchronized void addImage(Image image, int id, int w, int h) {
- head = MediaEntry.insert(head,
- new ImageMediaEntry(this, image, id, w, h));
- }
-
- /**
- * Check to see if all images have finished loading, but do not start
- * loading the images if they are not already loading.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @return true if all images have finished loading or have an error
- * @see #checkAll(boolean)
- * @see #checkID
- * @see #isErrorAny
- * @see #isErrorID
- */
- public boolean checkAll() {
- return checkAll(false);
- }
-
- /**
- * Check to see if all images have finished loading and start loading
- * any images that are not yet being loaded if load is true.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @param load start loading the images if this parameter is true
- * @return true if all images have finished loading or have an error
- * @see #isErrorAny
- * @see #isErrorID
- * @see #checkID(int, boolean)
- * @see #checkAll()
- */
- public synchronized boolean checkAll(boolean load) {
- MediaEntry cur = head;
- boolean done = true;
- while (cur != null) {
- if (!cur.isDone()) {
- done = false;
- if (load) {
- cur.startLoad();
- }
- }
- cur = cur.next;
- }
- return done;
- }
-
- /**
- * Check the error status of all of the images.
- * @return true if any of the images had an error during loading
- * @see #isErrorID
- */
- public synchronized boolean isErrorAny() {
- MediaEntry cur = head;
- while (cur != null) {
- if (cur.isErrored()) {
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
-
- /**
- * Start loading all images and wait until they have finished loading
- * or receive an error.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @see #waitForID
- * @see #isErrorAny
- * @see #isErrorID
- */
- public synchronized void waitForAll() throws InterruptedException {
- while (true) {
- if (checkAll(true)) {
- return;
- }
- wait();
- }
- }
-
- /**
- * Check to see if all images tagged with the indicated ID have
- * finished loading, but do not start loading the images if they
- * are not already loading.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @param id the identifier used to determine which images to check
- * @return true if all tagged images have finished loading or have an error
- * @see #checkID(int, boolean)
- * @see #checkAll
- * @see #isErrorAny
- * @see #isErrorID
- */
- public boolean checkID(int id) {
- return checkID(id, false);
- }
-
- /**
- * Check to see if all images tagged with the indicated ID have
- * finished loading and start loading any images with that ID that
- * are not yet being loaded if load is true.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @param id the identifier used to determine which images to check
- * @param load start loading the images if this parameter is true
- * @return true if all tagged images have finished loading or have an error
- * @see #checkID(int)
- * @see #checkAll
- * @see #isErrorAny
- * @see #isErrorID
- */
- public synchronized boolean checkID(int id, boolean load) {
- MediaEntry cur = head;
- boolean done = true;
- while (cur != null) {
- if (cur.getID() == id && !cur.isDone()) {
- done = false;
- if (load) {
- cur.startLoad();
- }
- }
- cur = cur.next;
- }
- return done;
- }
-
- /**
- * Check the error status of all of the images with the specified ID.
- * @param id the identifier used to determine which images to check
- * @return true if any of the tagged images had an error during loading
- * @see #isErrorAny
- */
- public synchronized boolean isErrorID(int id) {
- MediaEntry cur = head;
- while (cur != null) {
- if (cur.getID() == id && cur.isErrored()) {
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
-
- /**
- * Start loading all images with the specified ID and wait until they
- * have finished loading or receive an error.
- * If there is an error while loading or scaling an image then that
- * image is considered "complete".
- * Use isErrorAny or isErrorID to check for errors.
- * @see #waitForAll
- * @see #isErrorAny
- * @see #isErrorID
- */
- public synchronized void waitForID(int id)
- throws InterruptedException
- {
- while (true) {
- if (checkID(id, true)) {
- return;
- }
- wait();
- }
- }
-
- synchronized void setDone() {
- notifyAll();
- }
- }
-
- abstract class MediaEntry {
- MediaTracker tracker;
- int ID;
- MediaEntry next;
-
- boolean loaded;
- boolean errored;
-
- MediaEntry(MediaTracker mt, int id) {
- tracker = mt;
- ID = id;
- }
-
- static MediaEntry insert(MediaEntry head, MediaEntry me) {
- MediaEntry cur = head;
- MediaEntry prev = null;
- while (cur != null) {
- if (cur.ID > me.ID) {
- break;
- }
- prev = cur;
- cur = cur.next;
- }
- me.next = cur;
- if (prev == null) {
- head = me;
- } else {
- prev.next = me;
- }
- return head;
- }
-
- int getID() {
- return ID;
- }
-
- abstract void startLoad();
-
- synchronized boolean isDone() {
- return loaded || errored;
- }
-
- synchronized boolean isLoaded() {
- return loaded;
- }
-
- synchronized boolean isErrored() {
- return errored;
- }
-
- void setLoaded() {
- synchronized (this) {
- loaded = true;
- }
- tracker.setDone();
- }
-
- void setError() {
- synchronized (this) {
- errored = true;
- }
- tracker.setDone();
- }
- }
-
- class ImageMediaEntry extends MediaEntry implements ImageObserver {
- Image image;
- int width;
- int height;
-
- ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
- super(mt, c);
- image = img;
- width = w;
- height = h;
- }
-
- void startLoad() {
- if (tracker.target.prepareImage(image, width, height, this)) {
- setLoaded();
- }
- }
-
- public boolean imageUpdate(Image img, int infoflags,
- int x, int y, int w, int h) {
- if ((infoflags & ERROR) != 0) {
- setError();
- return false;
- }
- if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
- setLoaded();
- return false;
- }
- return true;
- }
- }
-