View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt;
27  
28  import java.awt.Component;
29  import java.awt.Image;
30  import java.awt.image.ImageObserver;
31  
32  /**
33   * The <code>MediaTracker</code> class is a utility class to track
34   * the status of a number of media objects. Media objects could
35   * include audio clips as well as images, though currently only
36   * images are supported.
37   * <p>
38   * To use a media tracker, create an instance of
39   * <code>MediaTracker</code> and call its <code>addImage</code>
40   * method for each image to be tracked. In addition, each image can
41   * be assigned a unique identifier. This identifier controls the
42   * priority order in which the images are fetched. It can also be used
43   * to identify unique subsets of the images that can be waited on
44   * independently. Images with a lower ID are loaded in preference to
45   * those with a higher ID number.
46   *
47   * <p>
48   *
49   * Tracking an animated image
50   * might not always be useful
51   * due to the multi-part nature of animated image
52   * loading and painting,
53   * but it is supported.
54   * <code>MediaTracker</code> treats an animated image
55   * as completely loaded
56   * when the first frame is completely loaded.
57   * At that point, the <code>MediaTracker</code>
58   * signals any waiters
59   * that the image is completely loaded.
60   * If no <code>ImageObserver</code>s are observing the image
61   * when the first frame has finished loading,
62   * the image might flush itself
63   * to conserve resources
64   * (see {@link Image#flush()}).
65   *
66   * <p>
67   * Here is an example of using <code>MediaTracker</code>:
68   * <p>
69   * <hr><blockquote><pre>{@code
70   * import java.applet.Applet;
71   * import java.awt.Color;
72   * import java.awt.Image;
73   * import java.awt.Graphics;
74   * import java.awt.MediaTracker;
75   *
76   * public class ImageBlaster extends Applet implements Runnable {
77   *      MediaTracker tracker;
78   *      Image bg;
79   *      Image anim[] = new Image[5];
80   *      int index;
81   *      Thread animator;
82   *
83   *      // Get the images for the background (id == 0)
84   *      // and the animation frames (id == 1)
85   *      // and add them to the MediaTracker
86   *      public void init() {
87   *          tracker = new MediaTracker(this);
88   *          bg = getImage(getDocumentBase(),
89   *                  "images/background.gif");
90   *          tracker.addImage(bg, 0);
91   *          for (int i = 0; i < 5; i++) {
92   *              anim[i] = getImage(getDocumentBase(),
93   *                      "images/anim"+i+".gif");
94   *              tracker.addImage(anim[i], 1);
95   *          }
96   *      }
97   *
98   *      // Start the animation thread.
99   *      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  * @author      Jim Graham
166  * @since       JDK1.0
167  */
168 public class MediaTracker implements java.io.Serializable {
169 
170     /**
171      * A given <code>Component</code> that will be
172      * tracked by a media tracker where the image will
173      * eventually be drawn.
174      *
175      * @serial
176      * @see #MediaTracker(Component)
177      */
178     Component target;
179     /**
180      * The head of the list of <code>Images</code> that is being
181      * tracked by the <code>MediaTracker</code>.
182      *
183      * @serial
184      * @see #addImage(Image, int)
185      * @see #removeImage(Image)
186      */
187     MediaEntry head;
188 
189     /*
190      * JDK 1.1 serialVersionUID
191      */
192     private static final long serialVersionUID = -483174189758638095L;
193 
194     /**
195      * Creates a media tracker to track images for a given component.
196      * @param     comp the component on which the images
197      *                     will eventually be drawn
198      */
199     public MediaTracker(Component comp) {
200         target = comp;
201     }
202 
203     /**
204      * Adds an image to the list of images being tracked by this media
205      * tracker. The image will eventually be rendered at its default
206      * (unscaled) size.
207      * @param     image   the image to be tracked
208      * @param     id      an identifier used to track this image
209      */
210     public void addImage(Image image, int id) {
211         addImage(image, id, -1, -1);
212     }
213 
214     /**
215      * Adds a scaled image to the list of images being tracked
216      * by this media tracker. The image will eventually be
217      * rendered at the indicated width and height.
218      *
219      * @param     image   the image to be tracked
220      * @param     id   an identifier that can be used to track this image
221      * @param     w    the width at which the image is rendered
222      * @param     h    the height at which the image is rendered
223      */
224     public synchronized void addImage(Image image, int id, int w, int h) {
225         head = MediaEntry.insert(head,
226                                  new ImageMediaEntry(this, image, id, w, h));
227     }
228 
229     /**
230      * Flag indicating that media is currently being loaded.
231      * @see         java.awt.MediaTracker#statusAll
232      * @see         java.awt.MediaTracker#statusID
233      */
234     public static final int LOADING = 1;
235 
236     /**
237      * Flag indicating that the downloading of media was aborted.
238      * @see         java.awt.MediaTracker#statusAll
239      * @see         java.awt.MediaTracker#statusID
240      */
241     public static final int ABORTED = 2;
242 
243     /**
244      * Flag indicating that the downloading of media encountered
245      * an error.
246      * @see         java.awt.MediaTracker#statusAll
247      * @see         java.awt.MediaTracker#statusID
248      */
249     public static final int ERRORED = 4;
250 
251     /**
252      * Flag indicating that the downloading of media was completed
253      * successfully.
254      * @see         java.awt.MediaTracker#statusAll
255      * @see         java.awt.MediaTracker#statusID
256      */
257     public static final int COMPLETE = 8;
258 
259     static final int DONE = (ABORTED | ERRORED | COMPLETE);
260 
261     /**
262      * Checks to see if all images being tracked by this media tracker
263      * have finished loading.
264      * <p>
265      * This method does not start loading the images if they are not
266      * already loading.
267      * <p>
268      * If there is an error while loading or scaling an image, then that
269      * image is considered to have finished loading. Use the
270      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
271      * check for errors.
272      * @return      <code>true</code> if all images have finished loading,
273      *                       have been aborted, or have encountered
274      *                       an error; <code>false</code> otherwise
275      * @see         java.awt.MediaTracker#checkAll(boolean)
276      * @see         java.awt.MediaTracker#checkID
277      * @see         java.awt.MediaTracker#isErrorAny
278      * @see         java.awt.MediaTracker#isErrorID
279      */
280     public boolean checkAll() {
281         return checkAll(false, true);
282     }
283 
284     /**
285      * Checks to see if all images being tracked by this media tracker
286      * have finished loading.
287      * <p>
288      * If the value of the <code>load</code> flag is <code>true</code>,
289      * then this method starts loading any images that are not yet
290      * being loaded.
291      * <p>
292      * If there is an error while loading or scaling an image, that
293      * image is considered to have finished loading. Use the
294      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
295      * check for errors.
296      * @param       load   if <code>true</code>, start loading any
297      *                       images that are not yet being loaded
298      * @return      <code>true</code> if all images have finished loading,
299      *                       have been aborted, or have encountered
300      *                       an error; <code>false</code> otherwise
301      * @see         java.awt.MediaTracker#checkID
302      * @see         java.awt.MediaTracker#checkAll()
303      * @see         java.awt.MediaTracker#isErrorAny()
304      * @see         java.awt.MediaTracker#isErrorID(int)
305      */
306     public boolean checkAll(boolean load) {
307         return checkAll(load, true);
308     }
309 
310     private synchronized boolean checkAll(boolean load, boolean verify) {
311         MediaEntry cur = head;
312         boolean done = true;
313         while (cur != null) {
314             if ((cur.getStatus(load, verify) & DONE) == 0) {
315                 done = false;
316             }
317             cur = cur.next;
318         }
319         return done;
320     }
321 
322     /**
323      * Checks the error status of all of the images.
324      * @return   <code>true</code> if any of the images tracked
325      *                  by this media tracker had an error during
326      *                  loading; <code>false</code> otherwise
327      * @see      java.awt.MediaTracker#isErrorID
328      * @see      java.awt.MediaTracker#getErrorsAny
329      */
330     public synchronized boolean isErrorAny() {
331         MediaEntry cur = head;
332         while (cur != null) {
333             if ((cur.getStatus(false, true) & ERRORED) != 0) {
334                 return true;
335             }
336             cur = cur.next;
337         }
338         return false;
339     }
340 
341     /**
342      * Returns a list of all media that have encountered an error.
343      * @return       an array of media objects tracked by this
344      *                        media tracker that have encountered
345      *                        an error, or <code>null</code> if
346      *                        there are none with errors
347      * @see          java.awt.MediaTracker#isErrorAny
348      * @see          java.awt.MediaTracker#getErrorsID
349      */
350     public synchronized Object[] getErrorsAny() {
351         MediaEntry cur = head;
352         int numerrors = 0;
353         while (cur != null) {
354             if ((cur.getStatus(false, true) & ERRORED) != 0) {
355                 numerrors++;
356             }
357             cur = cur.next;
358         }
359         if (numerrors == 0) {
360             return null;
361         }
362         Object errors[] = new Object[numerrors];
363         cur = head;
364         numerrors = 0;
365         while (cur != null) {
366             if ((cur.getStatus(false, false) & ERRORED) != 0) {
367                 errors[numerrors++] = cur.getMedia();
368             }
369             cur = cur.next;
370         }
371         return errors;
372     }
373 
374     /**
375      * Starts loading all images tracked by this media tracker. This
376      * method waits until all the images being tracked have finished
377      * loading.
378      * <p>
379      * If there is an error while loading or scaling an image, then that
380      * image is considered to have finished loading. Use the
381      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
382      * check for errors.
383      * @see         java.awt.MediaTracker#waitForID(int)
384      * @see         java.awt.MediaTracker#waitForAll(long)
385      * @see         java.awt.MediaTracker#isErrorAny
386      * @see         java.awt.MediaTracker#isErrorID
387      * @exception   InterruptedException  if any thread has
388      *                                     interrupted this thread
389      */
390     public void waitForAll() throws InterruptedException {
391         waitForAll(0);
392     }
393 
394     /**
395      * Starts loading all images tracked by this media tracker. This
396      * method waits until all the images being tracked have finished
397      * loading, or until the length of time specified in milliseconds
398      * by the <code>ms</code> argument has passed.
399      * <p>
400      * If there is an error while loading or scaling an image, then
401      * that image is considered to have finished loading. Use the
402      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
403      * check for errors.
404      * @param       ms       the number of milliseconds to wait
405      *                       for the loading to complete
406      * @return      <code>true</code> if all images were successfully
407      *                       loaded; <code>false</code> otherwise
408      * @see         java.awt.MediaTracker#waitForID(int)
409      * @see         java.awt.MediaTracker#waitForAll(long)
410      * @see         java.awt.MediaTracker#isErrorAny
411      * @see         java.awt.MediaTracker#isErrorID
412      * @exception   InterruptedException  if any thread has
413      *                                     interrupted this thread.
414      */
415     public synchronized boolean waitForAll(long ms)
416         throws InterruptedException
417     {
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, boolean verify)
528     {
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             {
535                 done = false;
536             }
537             cur = cur.next;
538         }
539         return done;
540     }
541 
542     /**
543      * Checks the error status of all of the images tracked by this
544      * media tracker with the specified identifier.
545      * @param        id   the identifier of the images to check
546      * @return       <code>true</code> if any of the images with the
547      *                          specified identifier had an error during
548      *                          loading; <code>false</code> otherwise
549      * @see          java.awt.MediaTracker#isErrorAny
550      * @see          java.awt.MediaTracker#getErrorsID
551      */
552     public synchronized boolean isErrorID(int id) {
553         MediaEntry cur = head;
554         while (cur != null) {
555             if (cur.getID() == id
556                 && (cur.getStatus(false, true) & ERRORED) != 0)
557             {
558                 return true;
559             }
560             cur = cur.next;
561         }
562         return false;
563     }
564 
565     /**
566      * Returns a list of media with the specified ID that
567      * have encountered an error.
568      * @param       id   the identifier of the images to check
569      * @return      an array of media objects tracked by this media
570      *                       tracker with the specified identifier
571      *                       that have encountered an error, or
572      *                       <code>null</code> if there are none with errors
573      * @see         java.awt.MediaTracker#isErrorID
574      * @see         java.awt.MediaTracker#isErrorAny
575      * @see         java.awt.MediaTracker#getErrorsAny
576      */
577     public synchronized Object[] getErrorsID(int id) {
578         MediaEntry cur = head;
579         int numerrors = 0;
580         while (cur != null) {
581             if (cur.getID() == id
582                 && (cur.getStatus(false, true) & ERRORED) != 0)
583             {
584                 numerrors++;
585             }
586             cur = cur.next;
587         }
588         if (numerrors == 0) {
589             return null;
590         }
591         Object errors[] = new Object[numerrors];
592         cur = head;
593         numerrors = 0;
594         while (cur != null) {
595             if (cur.getID() == id
596                 && (cur.getStatus(false, false) & ERRORED) != 0)
597             {
598                 errors[numerrors++] = cur.getMedia();
599             }
600             cur = cur.next;
601         }
602         return errors;
603     }
604 
605     /**
606      * Starts loading all images tracked by this media tracker with the
607      * specified identifier. This method waits until all the images with
608      * the specified identifier have finished loading.
609      * <p>
610      * If there is an error while loading or scaling an image, then that
611      * image is considered to have finished loading. Use the
612      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
613      * check for errors.
614      * @param         id   the identifier of the images to check
615      * @see           java.awt.MediaTracker#waitForAll
616      * @see           java.awt.MediaTracker#isErrorAny()
617      * @see           java.awt.MediaTracker#isErrorID(int)
618      * @exception     InterruptedException  if any thread has
619      *                          interrupted this thread.
620      */
621     public void waitForID(int id) throws InterruptedException {
622         waitForID(id, 0);
623     }
624 
625     /**
626      * Starts loading all images tracked by this media tracker with the
627      * specified identifier. This method waits until all the images with
628      * the specified identifier have finished loading, or until the
629      * length of time specified in milliseconds by the <code>ms</code>
630      * argument has passed.
631      * <p>
632      * If there is an error while loading or scaling an image, then that
633      * image is considered to have finished loading. Use the
634      * <code>statusID</code>, <code>isErrorID</code>, and
635      * <code>isErrorAny</code> methods to check for errors.
636      * @param         id   the identifier of the images to check
637      * @param         ms   the length of time, in milliseconds, to wait
638      *                           for the loading to complete
639      * @see           java.awt.MediaTracker#waitForAll
640      * @see           java.awt.MediaTracker#waitForID(int)
641      * @see           java.awt.MediaTracker#statusID
642      * @see           java.awt.MediaTracker#isErrorAny()
643      * @see           java.awt.MediaTracker#isErrorID(int)
644      * @exception     InterruptedException  if any thread has
645      *                          interrupted this thread.
646      */
647     public synchronized boolean waitForID(int id, long ms)
648         throws InterruptedException
649     {
650         long end = System.currentTimeMillis() + ms;
651         boolean first = true;
652         while (true) {
653             int status = statusID(id, first, first);
654             if ((status & LOADING) == 0) {
655                 return (status == COMPLETE);
656             }
657             first = false;
658             long timeout;
659             if (ms == 0) {
660                 timeout = 0;
661             } else {
662                 timeout = end - System.currentTimeMillis();
663                 if (timeout <= 0) {
664                     return false;
665                 }
666             }
667             wait(timeout);
668         }
669     }
670 
671     /**
672      * Calculates and returns the bitwise inclusive <b>OR</b> of the
673      * status of all media with the specified identifier that are
674      * tracked by this media tracker.
675      * <p>
676      * Possible flags defined by the
677      * <code>MediaTracker</code> class are <code>LOADING</code>,
678      * <code>ABORTED</code>, <code>ERRORED</code>, and
679      * <code>COMPLETE</code>. An image that hasn't started
680      * loading has zero as its status.
681      * <p>
682      * If the value of <code>load</code> is <code>true</code>, then
683      * this method starts loading any images that are not yet being loaded.
684      * @param        id   the identifier of the images to check
685      * @param        load   if <code>true</code>, start loading
686      *                            any images that are not yet being loaded
687      * @return       the bitwise inclusive <b>OR</b> of the status of
688      *                            all of the media with the specified
689      *                            identifier that are being tracked
690      * @see          java.awt.MediaTracker#statusAll(boolean)
691      * @see          java.awt.MediaTracker#LOADING
692      * @see          java.awt.MediaTracker#ABORTED
693      * @see          java.awt.MediaTracker#ERRORED
694      * @see          java.awt.MediaTracker#COMPLETE
695      */
696     public int statusID(int id, boolean load) {
697         return statusID(id, load, true);
698     }
699 
700     private synchronized int statusID(int id, boolean load, boolean verify) {
701         MediaEntry cur = head;
702         int status = 0;
703         while (cur != null) {
704             if (cur.getID() == id) {
705                 status = status | cur.getStatus(load, verify);
706             }
707             cur = cur.next;
708         }
709         return status;
710     }
711 
712     /**
713      * Removes the specified image from this media tracker.
714      * All instances of the specified image are removed,
715      * regardless of scale or ID.
716      * @param   image     the image to be removed
717      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
718      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
719      * @since   JDK1.1
720      */
721     public synchronized void removeImage(Image image) {
722         MediaEntry cur = head;
723         MediaEntry prev = null;
724         while (cur != null) {
725             MediaEntry next = cur.next;
726             if (cur.getMedia() == image) {
727                 if (prev == null) {
728                     head = next;
729                 } else {
730                     prev.next = next;
731                 }
732                 cur.cancel();
733             } else {
734                 prev = cur;
735             }
736             cur = next;
737         }
738         notifyAll();    // Notify in case remaining images are "done".
739     }
740 
741     /**
742      * Removes the specified image from the specified tracking
743      * ID of this media tracker.
744      * All instances of <code>Image</code> being tracked
745      * under the specified ID are removed regardless of scale.
746      * @param      image the image to be removed
747      * @param      id the tracking ID from which to remove the image
748      * @see        java.awt.MediaTracker#removeImage(java.awt.Image)
749      * @see        java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
750      * @since      JDK1.1
751      */
752     public synchronized void removeImage(Image image, int id) {
753         MediaEntry cur = head;
754         MediaEntry prev = null;
755         while (cur != null) {
756             MediaEntry next = cur.next;
757             if (cur.getID() == id && cur.getMedia() == image) {
758                 if (prev == null) {
759                     head = next;
760                 } else {
761                     prev.next = next;
762                 }
763                 cur.cancel();
764             } else {
765                 prev = cur;
766             }
767             cur = next;
768         }
769         notifyAll();    // Notify in case remaining images are "done".
770     }
771 
772     /**
773      * Removes the specified image with the specified
774      * width, height, and ID from this media tracker.
775      * Only the specified instance (with any duplicates) is removed.
776      * @param   image the image to be removed
777      * @param   id the tracking ID from which to remove the image
778      * @param   width the width to remove (-1 for unscaled)
779      * @param   height the height to remove (-1 for unscaled)
780      * @see     java.awt.MediaTracker#removeImage(java.awt.Image)
781      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
782      * @since   JDK1.1
783      */
784     public synchronized void removeImage(Image image, int id,
785                                          int width, int height) {
786         MediaEntry cur = head;
787         MediaEntry prev = null;
788         while (cur != null) {
789             MediaEntry next = cur.next;
790             if (cur.getID() == id && cur instanceof ImageMediaEntry
791                 && ((ImageMediaEntry) cur).matches(image, width, height))
792             {
793                 if (prev == null) {
794                     head = next;
795                 } else {
796                     prev.next = next;
797                 }
798                 cur.cancel();
799             } else {
800                 prev = cur;
801             }
802             cur = next;
803         }
804         notifyAll();    // Notify in case remaining images are "done".
805     }
806 
807     synchronized void setDone() {
808         notifyAll();
809     }
810 }
811 
812 abstract class MediaEntry {
813     MediaTracker tracker;
814     int ID;
815     MediaEntry next;
816 
817     int status;
818     boolean cancelled;
819 
820     MediaEntry(MediaTracker mt, int id) {
821         tracker = mt;
822         ID = id;
823     }
824 
825     abstract Object getMedia();
826 
827     static MediaEntry insert(MediaEntry head, MediaEntry me) {
828         MediaEntry cur = head;
829         MediaEntry prev = null;
830         while (cur != null) {
831             if (cur.ID > me.ID) {
832                 break;
833             }
834             prev = cur;
835             cur = cur.next;
836         }
837         me.next = cur;
838         if (prev == null) {
839             head = me;
840         } else {
841             prev.next = me;
842         }
843         return head;
844     }
845 
846     int getID() {
847         return ID;
848     }
849 
850     abstract void startLoad();
851 
852     void cancel() {
853         cancelled = true;
854     }
855 
856     static final int LOADING = MediaTracker.LOADING;
857     static final int ABORTED = MediaTracker.ABORTED;
858     static final int ERRORED = MediaTracker.ERRORED;
859     static final int COMPLETE = MediaTracker.COMPLETE;
860 
861     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
862     static final int DONE = (ABORTED | ERRORED | COMPLETE);
863 
864     synchronized int getStatus(boolean doLoad, boolean doVerify) {
865         if (doLoad && ((status & LOADSTARTED) == 0)) {
866             status = (status & ~ABORTED) | LOADING;
867             startLoad();
868         }
869         return status;
870     }
871 
872     void setStatus(int flag) {
873         synchronized (this) {
874             status = flag;
875         }
876         tracker.setDone();
877     }
878 }
879 
880 class ImageMediaEntry extends MediaEntry implements ImageObserver,
881 java.io.Serializable {
882     Image image;
883     int width;
884     int height;
885 
886     /*
887      * JDK 1.1 serialVersionUID
888      */
889     private static final long serialVersionUID = 4739377000350280650L;
890 
891     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
892         super(mt, c);
893         image = img;
894         width = w;
895         height = h;
896     }
897 
898     boolean matches(Image img, int w, int h) {
899         return (image == img && width == w && height == h);
900     }
901 
902     Object getMedia() {
903         return image;
904     }
905 
906     synchronized int getStatus(boolean doLoad, boolean doVerify) {
907         if (doVerify) {
908             int flags = tracker.target.checkImage(image, width, height, null);
909             int s = parseflags(flags);
910             if (s == 0) {
911                 if ((status & (ERRORED | COMPLETE)) != 0) {
912                     setStatus(ABORTED);
913                 }
914             } else if (s != status) {
915                 setStatus(s);
916             }
917         }
918         return super.getStatus(doLoad, doVerify);
919     }
920 
921     void startLoad() {
922         if (tracker.target.prepareImage(image, width, height, this)) {
923             setStatus(COMPLETE);
924         }
925     }
926 
927     int parseflags(int infoflags) {
928         if ((infoflags & ERROR) != 0) {
929             return ERRORED;
930         } else if ((infoflags & ABORT) != 0) {
931             return ABORTED;
932         } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
933             return COMPLETE;
934         }
935         return 0;
936     }
937 
938     public boolean imageUpdate(Image img, int infoflags,
939                                int x, int y, int w, int h) {
940         if (cancelled) {
941             return false;
942         }
943         int s = parseflags(infoflags);
944         if (s != 0 && s != status) {
945             setStatus(s);
946         }
947         return ((status & LOADING) != 0);
948     }
949 }