1 /*
2 * Copyright 1995-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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>
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 frrom 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 }