001: /*
002: * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.awt;
027:
028: import java.awt.Component;
029: import java.awt.Image;
030: import java.awt.image.ImageObserver;
031:
032: /**
033: * The <code>MediaTracker</code> class is a utility class to track
034: * the status of a number of media objects. Media objects could
035: * include audio clips as well as images, though currently only
036: * images are supported.
037: * <p>
038: * To use a media tracker, create an instance of
039: * <code>MediaTracker</code> and call its <code>addImage</code>
040: * method for each image to be tracked. In addition, each image can
041: * be assigned a unique identifier. This identifier controls the
042: * priority order in which the images are fetched. It can also be used
043: * to identify unique subsets of the images that can be waited on
044: * independently. Images with a lower ID are loaded in preference to
045: * those with a higher ID number.
046: *
047: * <p>
048: *
049: * Tracking an animated image
050: * might not always be useful
051: * due to the multi-part nature of animated image
052: * loading and painting,
053: * but it is supported.
054: * <code>MediaTracker</code> treats an animated image
055: * as completely loaded
056: * when the first frame is completely loaded.
057: * At that point, the <code>MediaTracker</code>
058: * signals any waiters
059: * that the image is completely loaded.
060: * If no <code>ImageObserver</code>s are observing the image
061: * when the first frame has finished loading,
062: * the image might flush itself
063: * to conserve resources
064: * (see {@link Image#flush()}).
065: *
066: * <p>
067: * Here is an example of using <code>MediaTracker</code>:
068: * <p>
069: * <hr><blockquote><pre>
070: * import java.applet.Applet;
071: * import java.awt.Color;
072: * import java.awt.Image;
073: * import java.awt.Graphics;
074: * import java.awt.MediaTracker;
075: *
076: * public class ImageBlaster extends Applet implements Runnable {
077: * MediaTracker tracker;
078: * Image bg;
079: * Image anim[] = new Image[5];
080: * int index;
081: * Thread animator;
082: *
083: * // Get the images for the background (id == 0)
084: * // and the animation frames (id == 1)
085: * // and add them to the MediaTracker
086: * public void init() {
087: * tracker = new MediaTracker(this);
088: * bg = getImage(getDocumentBase(),
089: * "images/background.gif");
090: * tracker.addImage(bg, 0);
091: * for (int i = 0; i < 5; i++) {
092: * anim[i] = getImage(getDocumentBase(),
093: * "images/anim"+i+".gif");
094: * tracker.addImage(anim[i], 1);
095: * }
096: * }
097: *
098: * // Start the animation thread.
099: * public void start() {
100: * animator = new Thread(this);
101: * animator.start();
102: * }
103: *
104: * // Stop the animation thread.
105: * public void stop() {
106: * animator = null;
107: * }
108: *
109: * // Run the animation thread.
110: * // First wait for the background image to fully load
111: * // and paint. Then wait for all of the animation
112: * // frames to finish loading. Finally, loop and
113: * // increment the animation frame index.
114: * public void run() {
115: * try {
116: * tracker.waitForID(0);
117: * tracker.waitForID(1);
118: * } catch (InterruptedException e) {
119: * return;
120: * }
121: * Thread me = Thread.currentThread();
122: * while (animator == me) {
123: * try {
124: * Thread.sleep(100);
125: * } catch (InterruptedException e) {
126: * break;
127: * }
128: * synchronized (this) {
129: * index++;
130: * if (index >= anim.length) {
131: * index = 0;
132: * }
133: * }
134: * repaint();
135: * }
136: * }
137: *
138: * // The background image fills the frame so we
139: * // don't need to clear the applet on repaints.
140: * // Just call the paint method.
141: * public void update(Graphics g) {
142: * paint(g);
143: * }
144: *
145: * // Paint a large red rectangle if there are any errors
146: * // loading the images. Otherwise always paint the
147: * // background so that it appears incrementally as it
148: * // is loading. Finally, only paint the current animation
149: * // frame if all of the frames (id == 1) are done loading,
150: * // so that we don't get partial animations.
151: * public void paint(Graphics g) {
152: * if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) {
153: * g.setColor(Color.red);
154: * g.fillRect(0, 0, size().width, size().height);
155: * return;
156: * }
157: * g.drawImage(bg, 0, 0, this);
158: * if (tracker.statusID(1, false) == MediaTracker.COMPLETE) {
159: * g.drawImage(anim[index], 10, 10, this);
160: * }
161: * }
162: * }
163: * </pre></blockquote><hr>
164: *
165: * @version 1.52, 06/05/07
166: * @author Jim Graham
167: * @since JDK1.0
168: */
169: public class MediaTracker implements java.io.Serializable {
170:
171: /**
172: * A given <code>Component</code> that will be
173: * tracked by a media tracker where the image will
174: * eventually be drawn.
175: *
176: * @serial
177: * @see #MediaTracker(Component)
178: */
179: Component target;
180: /**
181: * The head of the list of <code>Images</code> that is being
182: * tracked by the <code>MediaTracker</code>.
183: *
184: * @serial
185: * @see #addImage(Image, int)
186: * @see #removeImage(Image)
187: */
188: MediaEntry head;
189:
190: /*
191: * JDK 1.1 serialVersionUID
192: */
193: private static final long serialVersionUID = -483174189758638095L;
194:
195: /**
196: * Creates a media tracker to track images for a given component.
197: * @param comp the component on which the images
198: * will eventually be drawn
199: */
200: public MediaTracker(Component comp) {
201: target = comp;
202: }
203:
204: /**
205: * Adds an image to the list of images being tracked by this media
206: * tracker. The image will eventually be rendered at its default
207: * (unscaled) size.
208: * @param image the image to be tracked
209: * @param id an identifier used to track this image
210: */
211: public void addImage(Image image, int id) {
212: addImage(image, id, -1, -1);
213: }
214:
215: /**
216: * Adds a scaled image to the list of images being tracked
217: * by this media tracker. The image will eventually be
218: * rendered at the indicated width and height.
219: *
220: * @param image the image to be tracked
221: * @param id an identifier that can be used to track this image
222: * @param w the width at which the image is rendered
223: * @param h the height at which the image is rendered
224: */
225: public synchronized void addImage(Image image, int id, int w, int h) {
226: head = MediaEntry.insert(head, new ImageMediaEntry(this , image,
227: id, w, h));
228: }
229:
230: /**
231: * Flag indicating that media is currently being loaded.
232: * @see java.awt.MediaTracker#statusAll
233: * @see java.awt.MediaTracker#statusID
234: */
235: public static final int LOADING = 1;
236:
237: /**
238: * Flag indicating that the downloading of media was aborted.
239: * @see java.awt.MediaTracker#statusAll
240: * @see java.awt.MediaTracker#statusID
241: */
242: public static final int ABORTED = 2;
243:
244: /**
245: * Flag indicating that the downloading of media encountered
246: * an error.
247: * @see java.awt.MediaTracker#statusAll
248: * @see java.awt.MediaTracker#statusID
249: */
250: public static final int ERRORED = 4;
251:
252: /**
253: * Flag indicating that the downloading of media was completed
254: * successfully.
255: * @see java.awt.MediaTracker#statusAll
256: * @see java.awt.MediaTracker#statusID
257: */
258: public static final int COMPLETE = 8;
259:
260: static final int DONE = (ABORTED | ERRORED | COMPLETE);
261:
262: /**
263: * Checks to see if all images being tracked by this media tracker
264: * have finished loading.
265: * <p>
266: * This method does not start loading the images if they are not
267: * already loading.
268: * <p>
269: * If there is an error while loading or scaling an image, then that
270: * image is considered to have finished loading. Use the
271: * <code>isErrorAny</code> or <code>isErrorID</code> methods to
272: * check for errors.
273: * @return <code>true</code> if all images have finished loading,
274: * have been aborted, or have encountered
275: * an error; <code>false</code> otherwise
276: * @see java.awt.MediaTracker#checkAll(boolean)
277: * @see java.awt.MediaTracker#checkID
278: * @see java.awt.MediaTracker#isErrorAny
279: * @see java.awt.MediaTracker#isErrorID
280: */
281: public boolean checkAll() {
282: return checkAll(false, true);
283: }
284:
285: /**
286: * Checks to see if all images being tracked by this media tracker
287: * have finished loading.
288: * <p>
289: * If the value of the <code>load</code> flag is <code>true</code>,
290: * then this method starts loading any images that are not yet
291: * being loaded.
292: * <p>
293: * If there is an error while loading or scaling an image, that
294: * image is considered to have finished loading. Use the
295: * <code>isErrorAny</code> and <code>isErrorID</code> methods to
296: * check for errors.
297: * @param load if <code>true</code>, start loading any
298: * images that are not yet being loaded
299: * @return <code>true</code> if all images have finished loading,
300: * have been aborted, or have encountered
301: * an error; <code>false</code> otherwise
302: * @see java.awt.MediaTracker#checkID
303: * @see java.awt.MediaTracker#checkAll()
304: * @see java.awt.MediaTracker#isErrorAny()
305: * @see java.awt.MediaTracker#isErrorID(int)
306: */
307: public boolean checkAll(boolean load) {
308: return checkAll(load, true);
309: }
310:
311: private synchronized boolean checkAll(boolean load, boolean verify) {
312: MediaEntry cur = head;
313: boolean done = true;
314: while (cur != null) {
315: if ((cur.getStatus(load, verify) & DONE) == 0) {
316: done = false;
317: }
318: cur = cur.next;
319: }
320: return done;
321: }
322:
323: /**
324: * Checks the error status of all of the images.
325: * @return <code>true</code> if any of the images tracked
326: * by this media tracker had an error during
327: * loading; <code>false</code> otherwise
328: * @see java.awt.MediaTracker#isErrorID
329: * @see java.awt.MediaTracker#getErrorsAny
330: */
331: public synchronized boolean isErrorAny() {
332: MediaEntry cur = head;
333: while (cur != null) {
334: if ((cur.getStatus(false, true) & ERRORED) != 0) {
335: return true;
336: }
337: cur = cur.next;
338: }
339: return false;
340: }
341:
342: /**
343: * Returns a list of all media that have encountered an error.
344: * @return an array of media objects tracked by this
345: * media tracker that have encountered
346: * an error, or <code>null</code> if
347: * there are none with errors
348: * @see java.awt.MediaTracker#isErrorAny
349: * @see java.awt.MediaTracker#getErrorsID
350: */
351: public synchronized Object[] getErrorsAny() {
352: MediaEntry cur = head;
353: int numerrors = 0;
354: while (cur != null) {
355: if ((cur.getStatus(false, true) & ERRORED) != 0) {
356: numerrors++;
357: }
358: cur = cur.next;
359: }
360: if (numerrors == 0) {
361: return null;
362: }
363: Object errors[] = new Object[numerrors];
364: cur = head;
365: numerrors = 0;
366: while (cur != null) {
367: if ((cur.getStatus(false, false) & ERRORED) != 0) {
368: errors[numerrors++] = cur.getMedia();
369: }
370: cur = cur.next;
371: }
372: return errors;
373: }
374:
375: /**
376: * Starts loading all images tracked by this media tracker. This
377: * method waits until all the images being tracked have finished
378: * loading.
379: * <p>
380: * If there is an error while loading or scaling an image, then that
381: * image is considered to have finished loading. Use the
382: * <code>isErrorAny</code> or <code>isErrorID</code> methods to
383: * check for errors.
384: * @see java.awt.MediaTracker#waitForID(int)
385: * @see java.awt.MediaTracker#waitForAll(long)
386: * @see java.awt.MediaTracker#isErrorAny
387: * @see java.awt.MediaTracker#isErrorID
388: * @exception InterruptedException if any thread has
389: * interrupted this thread
390: */
391: public void waitForAll() throws InterruptedException {
392: waitForAll(0);
393: }
394:
395: /**
396: * Starts loading all images tracked by this media tracker. This
397: * method waits until all the images being tracked have finished
398: * loading, or until the length of time specified in milliseconds
399: * by the <code>ms</code> argument has passed.
400: * <p>
401: * If there is an error while loading or scaling an image, then
402: * that image is considered to have finished loading. Use the
403: * <code>isErrorAny</code> or <code>isErrorID</code> methods to
404: * check for errors.
405: * @param ms the number of milliseconds to wait
406: * for the loading to complete
407: * @return <code>true</code> if all images were successfully
408: * loaded; <code>false</code> otherwise
409: * @see java.awt.MediaTracker#waitForID(int)
410: * @see java.awt.MediaTracker#waitForAll(long)
411: * @see java.awt.MediaTracker#isErrorAny
412: * @see java.awt.MediaTracker#isErrorID
413: * @exception InterruptedException if any thread has
414: * interrupted this thread.
415: */
416: public synchronized boolean waitForAll(long ms)
417: throws InterruptedException {
418: long end = System.currentTimeMillis() + ms;
419: boolean first = true;
420: while (true) {
421: int status = statusAll(first, first);
422: if ((status & LOADING) == 0) {
423: return (status == COMPLETE);
424: }
425: first = false;
426: long timeout;
427: if (ms == 0) {
428: timeout = 0;
429: } else {
430: timeout = end - System.currentTimeMillis();
431: if (timeout <= 0) {
432: return false;
433: }
434: }
435: wait(timeout);
436: }
437: }
438:
439: /**
440: * Calculates and returns the bitwise inclusive <b>OR</b> of the
441: * status of all media that are tracked by this media tracker.
442: * <p>
443: * Possible flags defined by the
444: * <code>MediaTracker</code> class are <code>LOADING</code>,
445: * <code>ABORTED</code>, <code>ERRORED</code>, and
446: * <code>COMPLETE</code>. An image that hasn't started
447: * loading has zero as its status.
448: * <p>
449: * If the value of <code>load</code> is <code>true</code>, then
450: * this method starts loading any images that are not yet being loaded.
451: *
452: * @param load if <code>true</code>, start loading
453: * any images that are not yet being loaded
454: * @return the bitwise inclusive <b>OR</b> of the status of
455: * all of the media being tracked
456: * @see java.awt.MediaTracker#statusID(int, boolean)
457: * @see java.awt.MediaTracker#LOADING
458: * @see java.awt.MediaTracker#ABORTED
459: * @see java.awt.MediaTracker#ERRORED
460: * @see java.awt.MediaTracker#COMPLETE
461: */
462: public int statusAll(boolean load) {
463: return statusAll(load, true);
464: }
465:
466: private synchronized int statusAll(boolean load, boolean verify) {
467: MediaEntry cur = head;
468: int status = 0;
469: while (cur != null) {
470: status = status | cur.getStatus(load, verify);
471: cur = cur.next;
472: }
473: return status;
474: }
475:
476: /**
477: * Checks to see if all images tracked by this media tracker that
478: * are tagged with the specified identifier have finished loading.
479: * <p>
480: * This method does not start loading the images if they are not
481: * already loading.
482: * <p>
483: * If there is an error while loading or scaling an image, then that
484: * image is considered to have finished loading. Use the
485: * <code>isErrorAny</code> or <code>isErrorID</code> methods to
486: * check for errors.
487: * @param id the identifier of the images to check
488: * @return <code>true</code> if all images have finished loading,
489: * have been aborted, or have encountered
490: * an error; <code>false</code> otherwise
491: * @see java.awt.MediaTracker#checkID(int, boolean)
492: * @see java.awt.MediaTracker#checkAll()
493: * @see java.awt.MediaTracker#isErrorAny()
494: * @see java.awt.MediaTracker#isErrorID(int)
495: */
496: public boolean checkID(int id) {
497: return checkID(id, false, true);
498: }
499:
500: /**
501: * Checks to see if all images tracked by this media tracker that
502: * are tagged with the specified identifier have finished loading.
503: * <p>
504: * If the value of the <code>load</code> flag is <code>true</code>,
505: * then this method starts loading any images that are not yet
506: * being loaded.
507: * <p>
508: * If there is an error while loading or scaling an image, then that
509: * image is considered to have finished loading. Use the
510: * <code>isErrorAny</code> or <code>isErrorID</code> methods to
511: * check for errors.
512: * @param id the identifier of the images to check
513: * @param load if <code>true</code>, start loading any
514: * images that are not yet being loaded
515: * @return <code>true</code> if all images have finished loading,
516: * have been aborted, or have encountered
517: * an error; <code>false</code> otherwise
518: * @see java.awt.MediaTracker#checkID(int, boolean)
519: * @see java.awt.MediaTracker#checkAll()
520: * @see java.awt.MediaTracker#isErrorAny()
521: * @see java.awt.MediaTracker#isErrorID(int)
522: */
523: public boolean checkID(int id, boolean load) {
524: return checkID(id, load, true);
525: }
526:
527: private synchronized boolean checkID(int id, boolean load,
528: boolean verify) {
529: MediaEntry cur = head;
530: boolean done = true;
531: while (cur != null) {
532: if (cur.getID() == id
533: && (cur.getStatus(load, verify) & DONE) == 0) {
534: done = false;
535: }
536: cur = cur.next;
537: }
538: return done;
539: }
540:
541: /**
542: * Checks the error status of all of the images tracked by this
543: * media tracker with the specified identifier.
544: * @param id the identifier of the images to check
545: * @return <code>true</code> if any of the images with the
546: * specified identifier had an error during
547: * loading; <code>false</code> otherwise
548: * @see java.awt.MediaTracker#isErrorAny
549: * @see java.awt.MediaTracker#getErrorsID
550: */
551: public synchronized boolean isErrorID(int id) {
552: MediaEntry cur = head;
553: while (cur != null) {
554: if (cur.getID() == id
555: && (cur.getStatus(false, true) & ERRORED) != 0) {
556: return true;
557: }
558: cur = cur.next;
559: }
560: return false;
561: }
562:
563: /**
564: * Returns a list of media with the specified ID that
565: * have encountered an error.
566: * @param id the identifier of the images to check
567: * @return an array of media objects tracked by this media
568: * tracker with the specified identifier
569: * that have encountered an error, or
570: * <code>null</code> if there are none with errors
571: * @see java.awt.MediaTracker#isErrorID
572: * @see java.awt.MediaTracker#isErrorAny
573: * @see java.awt.MediaTracker#getErrorsAny
574: */
575: public synchronized Object[] getErrorsID(int id) {
576: MediaEntry cur = head;
577: int numerrors = 0;
578: while (cur != null) {
579: if (cur.getID() == id
580: && (cur.getStatus(false, true) & ERRORED) != 0) {
581: numerrors++;
582: }
583: cur = cur.next;
584: }
585: if (numerrors == 0) {
586: return null;
587: }
588: Object errors[] = new Object[numerrors];
589: cur = head;
590: numerrors = 0;
591: while (cur != null) {
592: if (cur.getID() == id
593: && (cur.getStatus(false, false) & ERRORED) != 0) {
594: errors[numerrors++] = cur.getMedia();
595: }
596: cur = cur.next;
597: }
598: return errors;
599: }
600:
601: /**
602: * Starts loading all images tracked by this media tracker with the
603: * specified identifier. This method waits until all the images with
604: * the specified identifier have finished loading.
605: * <p>
606: * If there is an error while loading or scaling an image, then that
607: * image is considered to have finished loading. Use the
608: * <code>isErrorAny</code> and <code>isErrorID</code> methods to
609: * check for errors.
610: * @param id the identifier of the images to check
611: * @see java.awt.MediaTracker#waitForAll
612: * @see java.awt.MediaTracker#isErrorAny()
613: * @see java.awt.MediaTracker#isErrorID(int)
614: * @exception InterruptedException if any thread has
615: * interrupted this thread.
616: */
617: public void waitForID(int id) throws InterruptedException {
618: waitForID(id, 0);
619: }
620:
621: /**
622: * Starts loading all images tracked by this media tracker with the
623: * specified identifier. This method waits until all the images with
624: * the specified identifier have finished loading, or until the
625: * length of time specified in milliseconds by the <code>ms</code>
626: * argument has passed.
627: * <p>
628: * If there is an error while loading or scaling an image, then that
629: * image is considered to have finished loading. Use the
630: * <code>statusID</code>, <code>isErrorID</code>, and
631: * <code>isErrorAny</code> methods to check for errors.
632: * @param id the identifier of the images to check
633: * @param ms the length of time, in milliseconds, to wait
634: * for the loading to complete
635: * @see java.awt.MediaTracker#waitForAll
636: * @see java.awt.MediaTracker#waitForID(int)
637: * @see java.awt.MediaTracker#statusID
638: * @see java.awt.MediaTracker#isErrorAny()
639: * @see java.awt.MediaTracker#isErrorID(int)
640: * @exception InterruptedException if any thread has
641: * interrupted this thread.
642: */
643: public synchronized boolean waitForID(int id, long ms)
644: throws InterruptedException {
645: long end = System.currentTimeMillis() + ms;
646: boolean first = true;
647: while (true) {
648: int status = statusID(id, first, first);
649: if ((status & LOADING) == 0) {
650: return (status == COMPLETE);
651: }
652: first = false;
653: long timeout;
654: if (ms == 0) {
655: timeout = 0;
656: } else {
657: timeout = end - System.currentTimeMillis();
658: if (timeout <= 0) {
659: return false;
660: }
661: }
662: wait(timeout);
663: }
664: }
665:
666: /**
667: * Calculates and returns the bitwise inclusive <b>OR</b> of the
668: * status of all media with the specified identifier that are
669: * tracked by this media tracker.
670: * <p>
671: * Possible flags defined by the
672: * <code>MediaTracker</code> class are <code>LOADING</code>,
673: * <code>ABORTED</code>, <code>ERRORED</code>, and
674: * <code>COMPLETE</code>. An image that hasn't started
675: * loading has zero as its status.
676: * <p>
677: * If the value of <code>load</code> is <code>true</code>, then
678: * this method starts loading any images that are not yet being loaded.
679: * @param id the identifier of the images to check
680: * @param load if <code>true</code>, start loading
681: * any images that are not yet being loaded
682: * @return the bitwise inclusive <b>OR</b> of the status of
683: * all of the media with the specified
684: * identifier that are being tracked
685: * @see java.awt.MediaTracker#statusAll(boolean)
686: * @see java.awt.MediaTracker#LOADING
687: * @see java.awt.MediaTracker#ABORTED
688: * @see java.awt.MediaTracker#ERRORED
689: * @see java.awt.MediaTracker#COMPLETE
690: */
691: public int statusID(int id, boolean load) {
692: return statusID(id, load, true);
693: }
694:
695: private synchronized int statusID(int id, boolean load,
696: boolean verify) {
697: MediaEntry cur = head;
698: int status = 0;
699: while (cur != null) {
700: if (cur.getID() == id) {
701: status = status | cur.getStatus(load, verify);
702: }
703: cur = cur.next;
704: }
705: return status;
706: }
707:
708: /**
709: * Removes the specified image from this media tracker.
710: * All instances of the specified image are removed,
711: * regardless of scale or ID.
712: * @param image the image to be removed
713: * @see java.awt.MediaTracker#removeImage(java.awt.Image, int)
714: * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
715: * @since JDK1.1
716: */
717: public synchronized void removeImage(Image image) {
718: MediaEntry cur = head;
719: MediaEntry prev = null;
720: while (cur != null) {
721: MediaEntry next = cur.next;
722: if (cur.getMedia() == image) {
723: if (prev == null) {
724: head = next;
725: } else {
726: prev.next = next;
727: }
728: cur.cancel();
729: } else {
730: prev = cur;
731: }
732: cur = next;
733: }
734: notifyAll(); // Notify in case remaining images are "done".
735: }
736:
737: /**
738: * Removes the specified image from the specified tracking
739: * ID of this media tracker.
740: * All instances of <code>Image</code> being tracked
741: * under the specified ID are removed regardless of scale.
742: * @param image the image to be removed
743: * @param id the tracking ID frrom which to remove the image
744: * @see java.awt.MediaTracker#removeImage(java.awt.Image)
745: * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
746: * @since JDK1.1
747: */
748: public synchronized void removeImage(Image image, int id) {
749: MediaEntry cur = head;
750: MediaEntry prev = null;
751: while (cur != null) {
752: MediaEntry next = cur.next;
753: if (cur.getID() == id && cur.getMedia() == image) {
754: if (prev == null) {
755: head = next;
756: } else {
757: prev.next = next;
758: }
759: cur.cancel();
760: } else {
761: prev = cur;
762: }
763: cur = next;
764: }
765: notifyAll(); // Notify in case remaining images are "done".
766: }
767:
768: /**
769: * Removes the specified image with the specified
770: * width, height, and ID from this media tracker.
771: * Only the specified instance (with any duplicates) is removed.
772: * @param image the image to be removed
773: * @param id the tracking ID from which to remove the image
774: * @param width the width to remove (-1 for unscaled)
775: * @param height the height to remove (-1 for unscaled)
776: * @see java.awt.MediaTracker#removeImage(java.awt.Image)
777: * @see java.awt.MediaTracker#removeImage(java.awt.Image, int)
778: * @since JDK1.1
779: */
780: public synchronized void removeImage(Image image, int id,
781: int width, int height) {
782: MediaEntry cur = head;
783: MediaEntry prev = null;
784: while (cur != null) {
785: MediaEntry next = cur.next;
786: if (cur.getID() == id
787: && cur instanceof ImageMediaEntry
788: && ((ImageMediaEntry) cur).matches(image, width,
789: height)) {
790: if (prev == null) {
791: head = next;
792: } else {
793: prev.next = next;
794: }
795: cur.cancel();
796: } else {
797: prev = cur;
798: }
799: cur = next;
800: }
801: notifyAll(); // Notify in case remaining images are "done".
802: }
803:
804: synchronized void setDone() {
805: notifyAll();
806: }
807: }
808:
809: abstract class MediaEntry {
810: MediaTracker tracker;
811: int ID;
812: MediaEntry next;
813:
814: int status;
815: boolean cancelled;
816:
817: MediaEntry(MediaTracker mt, int id) {
818: tracker = mt;
819: ID = id;
820: }
821:
822: abstract Object getMedia();
823:
824: static MediaEntry insert(MediaEntry head, MediaEntry me) {
825: MediaEntry cur = head;
826: MediaEntry prev = null;
827: while (cur != null) {
828: if (cur.ID > me.ID) {
829: break;
830: }
831: prev = cur;
832: cur = cur.next;
833: }
834: me.next = cur;
835: if (prev == null) {
836: head = me;
837: } else {
838: prev.next = me;
839: }
840: return head;
841: }
842:
843: int getID() {
844: return ID;
845: }
846:
847: abstract void startLoad();
848:
849: void cancel() {
850: cancelled = true;
851: }
852:
853: static final int LOADING = MediaTracker.LOADING;
854: static final int ABORTED = MediaTracker.ABORTED;
855: static final int ERRORED = MediaTracker.ERRORED;
856: static final int COMPLETE = MediaTracker.COMPLETE;
857:
858: static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
859: static final int DONE = (ABORTED | ERRORED | COMPLETE);
860:
861: synchronized int getStatus(boolean doLoad, boolean doVerify) {
862: if (doLoad && ((status & LOADSTARTED) == 0)) {
863: status = (status & ~ABORTED) | LOADING;
864: startLoad();
865: }
866: return status;
867: }
868:
869: void setStatus(int flag) {
870: synchronized (this ) {
871: status = flag;
872: }
873: tracker.setDone();
874: }
875: }
876:
877: class ImageMediaEntry extends MediaEntry implements ImageObserver,
878: java.io.Serializable {
879: Image image;
880: int width;
881: int height;
882:
883: /*
884: * JDK 1.1 serialVersionUID
885: */
886: private static final long serialVersionUID = 4739377000350280650L;
887:
888: ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
889: super (mt, c);
890: image = img;
891: width = w;
892: height = h;
893: }
894:
895: boolean matches(Image img, int w, int h) {
896: return (image == img && width == w && height == h);
897: }
898:
899: Object getMedia() {
900: return image;
901: }
902:
903: synchronized int getStatus(boolean doLoad, boolean doVerify) {
904: if (doVerify) {
905: int flags = tracker.target.checkImage(image, width, height,
906: null);
907: int s = parseflags(flags);
908: if (s == 0) {
909: if ((status & (ERRORED | COMPLETE)) != 0) {
910: setStatus(ABORTED);
911: }
912: } else if (s != status) {
913: setStatus(s);
914: }
915: }
916: return super .getStatus(doLoad, doVerify);
917: }
918:
919: void startLoad() {
920: if (tracker.target.prepareImage(image, width, height, this )) {
921: setStatus(COMPLETE);
922: }
923: }
924:
925: int parseflags(int infoflags) {
926: if ((infoflags & ERROR) != 0) {
927: return ERRORED;
928: } else if ((infoflags & ABORT) != 0) {
929: return ABORTED;
930: } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
931: return COMPLETE;
932: }
933: return 0;
934: }
935:
936: public boolean imageUpdate(Image img, int infoflags, int x, int y,
937: int w, int h) {
938: if (cancelled) {
939: return false;
940: }
941: int s = parseflags(infoflags);
942: if (s != 0 && s != status) {
943: setStatus(s);
944: }
945: return ((status & LOADING) != 0);
946: }
947: }
|