1 /* 2 * Copyright (c) 1995, 2007, 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 import sun.awt.image.MultiResolutionToolkitImage; 32 33 /** 34 * The <code>MediaTracker</code> class is a utility class to track 35 * the status of a number of media objects. Media objects could 36 * include audio clips as well as images, though currently only 37 * images are supported. 38 * <p> 39 * To use a media tracker, create an instance of 40 * <code>MediaTracker</code> and call its <code>addImage</code> 41 * method for each image to be tracked. In addition, each image can 42 * be assigned a unique identifier. This identifier controls the 43 * priority order in which the images are fetched. It can also be used 44 * to identify unique subsets of the images that can be waited on 45 * independently. Images with a lower ID are loaded in preference to 46 * those with a higher ID number. 47 * 48 * <p> 49 * 50 * Tracking an animated image 51 * might not always be useful 52 * due to the multi-part nature of animated image 53 * loading and painting, 54 * but it is supported. 55 * <code>MediaTracker</code> treats an animated image 56 * as completely loaded 57 * when the first frame is completely loaded. 58 * At that point, the <code>MediaTracker</code> 59 * signals any waiters 60 * that the image is completely loaded. 61 * If no <code>ImageObserver</code>s are observing the image 62 * when the first frame has finished loading, 63 * the image might flush itself 64 * to conserve resources 65 * (see {@link Image#flush()}). 66 * 67 * <p> 68 * Here is an example of using <code>MediaTracker</code>: 69 * <p> 70 * <hr><blockquote><pre> 71 * import java.applet.Applet; 72 * import java.awt.Color; 73 * import java.awt.Image; 74 * import java.awt.Graphics; 75 * import java.awt.MediaTracker; 76 * 77 * public class ImageBlaster extends Applet implements Runnable { 78 * MediaTracker tracker; 79 * Image bg; 80 * Image anim[] = new Image[5]; 81 * int index; 82 * Thread animator; 83 * 84 * // Get the images for the background (id == 0) 85 * // and the animation frames (id == 1) 86 * // and add them to the MediaTracker 87 * public void init() { 88 * tracker = new MediaTracker(this); 89 * bg = getImage(getDocumentBase(), 90 * "images/background.gif"); 91 * tracker.addImage(bg, 0); 92 * for (int i = 0; i < 5; i++) { 93 * anim[i] = getImage(getDocumentBase(), 94 * "images/anim"+i+".gif"); 95 * tracker.addImage(anim[i], 1); 96 * } 97 * } 98 * 99 * // Start the animation thread. 100 * public void start() { 101 * animator = new Thread(this); 102 * animator.start(); 103 * } 104 * 105 * // Stop the animation thread. 106 * public void stop() { 107 * animator = null; 108 * } 109 * 110 * // Run the animation thread. 111 * // First wait for the background image to fully load 112 * // and paint. Then wait for all of the animation 113 * // frames to finish loading. Finally, loop and 114 * // increment the animation frame index. 115 * public void run() { 116 * try { 117 * tracker.waitForID(0); 118 * tracker.waitForID(1); 119 * } catch (InterruptedException e) { 120 * return; 121 * } 122 * Thread me = Thread.currentThread(); 123 * while (animator == me) { 124 * try { 125 * Thread.sleep(100); 126 * } catch (InterruptedException e) { 127 * break; 128 * } 129 * synchronized (this) { 130 * index++; 131 * if (index >= anim.length) { 132 * index = 0; 133 * } 134 * } 135 * repaint(); 136 * } 137 * } 138 * 139 * // The background image fills the frame so we 140 * // don't need to clear the applet on repaints. 141 * // Just call the paint method. 142 * public void update(Graphics g) { 143 * paint(g); 144 * } 145 * 146 * // Paint a large red rectangle if there are any errors 147 * // loading the images. Otherwise always paint the 148 * // background so that it appears incrementally as it 149 * // is loading. Finally, only paint the current animation 150 * // frame if all of the frames (id == 1) are done loading, 151 * // so that we don't get partial animations. 152 * public void paint(Graphics g) { 153 * if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) { 154 * g.setColor(Color.red); 155 * g.fillRect(0, 0, size().width, size().height); 156 * return; 157 * } 158 * g.drawImage(bg, 0, 0, this); 159 * if (tracker.statusID(1, false) == MediaTracker.COMPLETE) { 160 * g.drawImage(anim[index], 10, 10, this); 161 * } 162 * } 163 * } 164 * </pre></blockquote><hr> 165 * 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 addImageImpl(image, id, w, h); 227 Image rvImage = getResolutionVariant(image); 228 if (rvImage != null) { 229 addImageImpl(rvImage, id, 2 * w, 2 * h); 230 } 231 } 232 233 private void addImageImpl(Image image, int id, int w, int h) { 234 head = MediaEntry.insert(head, 235 new ImageMediaEntry(this, image, id, w, h)); 236 } 237 /** 238 * Flag indicating that media is currently being loaded. 239 * @see java.awt.MediaTracker#statusAll 240 * @see java.awt.MediaTracker#statusID 241 */ 242 public static final int LOADING = 1; 243 244 /** 245 * Flag indicating that the downloading of media was aborted. 246 * @see java.awt.MediaTracker#statusAll 247 * @see java.awt.MediaTracker#statusID 248 */ 249 public static final int ABORTED = 2; 250 251 /** 252 * Flag indicating that the downloading of media encountered 253 * an error. 254 * @see java.awt.MediaTracker#statusAll 255 * @see java.awt.MediaTracker#statusID 256 */ 257 public static final int ERRORED = 4; 258 259 /** 260 * Flag indicating that the downloading of media was completed 261 * successfully. 262 * @see java.awt.MediaTracker#statusAll 263 * @see java.awt.MediaTracker#statusID 264 */ 265 public static final int COMPLETE = 8; 266 267 static final int DONE = (ABORTED | ERRORED | COMPLETE); 268 269 /** 270 * Checks to see if all images being tracked by this media tracker 271 * have finished loading. 272 * <p> 273 * This method does not start loading the images if they are not 274 * already loading. 275 * <p> 276 * If there is an error while loading or scaling an image, then that 277 * image is considered to have finished loading. Use the 278 * <code>isErrorAny</code> or <code>isErrorID</code> methods to 279 * check for errors. 280 * @return <code>true</code> if all images have finished loading, 281 * have been aborted, or have encountered 282 * an error; <code>false</code> otherwise 283 * @see java.awt.MediaTracker#checkAll(boolean) 284 * @see java.awt.MediaTracker#checkID 285 * @see java.awt.MediaTracker#isErrorAny 286 * @see java.awt.MediaTracker#isErrorID 287 */ 288 public boolean checkAll() { 289 return checkAll(false, true); 290 } 291 292 /** 293 * Checks to see if all images being tracked by this media tracker 294 * have finished loading. 295 * <p> 296 * If the value of the <code>load</code> flag is <code>true</code>, 297 * then this method starts loading any images that are not yet 298 * being loaded. 299 * <p> 300 * If there is an error while loading or scaling an image, that 301 * image is considered to have finished loading. Use the 302 * <code>isErrorAny</code> and <code>isErrorID</code> methods to 303 * check for errors. 304 * @param load if <code>true</code>, start loading any 305 * images that are not yet being loaded 306 * @return <code>true</code> if all images have finished loading, 307 * have been aborted, or have encountered 308 * an error; <code>false</code> otherwise 309 * @see java.awt.MediaTracker#checkID 310 * @see java.awt.MediaTracker#checkAll() 311 * @see java.awt.MediaTracker#isErrorAny() 312 * @see java.awt.MediaTracker#isErrorID(int) 313 */ 314 public boolean checkAll(boolean load) { 315 return checkAll(load, true); 316 } 317 318 private synchronized boolean checkAll(boolean load, boolean verify) { 319 MediaEntry cur = head; 320 boolean done = true; 321 while (cur != null) { 322 if ((cur.getStatus(load, verify) & DONE) == 0) { 323 done = false; 324 } 325 cur = cur.next; 326 } 327 return done; 328 } 329 330 /** 331 * Checks the error status of all of the images. 332 * @return <code>true</code> if any of the images tracked 333 * by this media tracker had an error during 334 * loading; <code>false</code> otherwise 335 * @see java.awt.MediaTracker#isErrorID 336 * @see java.awt.MediaTracker#getErrorsAny 337 */ 338 public synchronized boolean isErrorAny() { 339 MediaEntry cur = head; 340 while (cur != null) { 341 if ((cur.getStatus(false, true) & ERRORED) != 0) { 342 return true; 343 } 344 cur = cur.next; 345 } 346 return false; 347 } 348 349 /** 350 * Returns a list of all media that have encountered an error. 351 * @return an array of media objects tracked by this 352 * media tracker that have encountered 353 * an error, or <code>null</code> if 354 * there are none with errors 355 * @see java.awt.MediaTracker#isErrorAny 356 * @see java.awt.MediaTracker#getErrorsID 357 */ 358 public synchronized Object[] getErrorsAny() { 359 MediaEntry cur = head; 360 int numerrors = 0; 361 while (cur != null) { 362 if ((cur.getStatus(false, true) & ERRORED) != 0) { 363 numerrors++; 364 } 365 cur = cur.next; 366 } 367 if (numerrors == 0) { 368 return null; 369 } 370 Object errors[] = new Object[numerrors]; 371 cur = head; 372 numerrors = 0; 373 while (cur != null) { 374 if ((cur.getStatus(false, false) & ERRORED) != 0) { 375 errors[numerrors++] = cur.getMedia(); 376 } 377 cur = cur.next; 378 } 379 return errors; 380 } 381 382 /** 383 * Starts loading all images tracked by this media tracker. This 384 * method waits until all the images being tracked have finished 385 * loading. 386 * <p> 387 * If there is an error while loading or scaling an image, then that 388 * image is considered to have finished loading. Use the 389 * <code>isErrorAny</code> or <code>isErrorID</code> methods to 390 * check for errors. 391 * @see java.awt.MediaTracker#waitForID(int) 392 * @see java.awt.MediaTracker#waitForAll(long) 393 * @see java.awt.MediaTracker#isErrorAny 394 * @see java.awt.MediaTracker#isErrorID 395 * @exception InterruptedException if any thread has 396 * interrupted this thread 397 */ 398 public void waitForAll() throws InterruptedException { 399 waitForAll(0); 400 } 401 402 /** 403 * Starts loading all images tracked by this media tracker. This 404 * method waits until all the images being tracked have finished 405 * loading, or until the length of time specified in milliseconds 406 * by the <code>ms</code> argument has passed. 407 * <p> 408 * If there is an error while loading or scaling an image, then 409 * that image is considered to have finished loading. Use the 410 * <code>isErrorAny</code> or <code>isErrorID</code> methods to 411 * check for errors. 412 * @param ms the number of milliseconds to wait 413 * for the loading to complete 414 * @return <code>true</code> if all images were successfully 415 * loaded; <code>false</code> otherwise 416 * @see java.awt.MediaTracker#waitForID(int) 417 * @see java.awt.MediaTracker#waitForAll(long) 418 * @see java.awt.MediaTracker#isErrorAny 419 * @see java.awt.MediaTracker#isErrorID 420 * @exception InterruptedException if any thread has 421 * interrupted this thread. 422 */ 423 public synchronized boolean waitForAll(long ms) 424 throws InterruptedException 425 { 426 long end = System.currentTimeMillis() + ms; 427 boolean first = true; 428 while (true) { 429 int status = statusAll(first, first); 430 if ((status & LOADING) == 0) { 431 return (status == COMPLETE); 432 } 433 first = false; 434 long timeout; 435 if (ms == 0) { 436 timeout = 0; 437 } else { 438 timeout = end - System.currentTimeMillis(); 439 if (timeout <= 0) { 440 return false; 441 } 442 } 443 wait(timeout); 444 } 445 } 446 447 /** 448 * Calculates and returns the bitwise inclusive <b>OR</b> of the 449 * status of all media that are tracked by this media tracker. 450 * <p> 451 * Possible flags defined by the 452 * <code>MediaTracker</code> class are <code>LOADING</code>, 453 * <code>ABORTED</code>, <code>ERRORED</code>, and 454 * <code>COMPLETE</code>. An image that hasn't started 455 * loading has zero as its status. 456 * <p> 457 * If the value of <code>load</code> is <code>true</code>, then 458 * this method starts loading any images that are not yet being loaded. 459 * 460 * @param load if <code>true</code>, start loading 461 * any images that are not yet being loaded 462 * @return the bitwise inclusive <b>OR</b> of the status of 463 * all of the media being tracked 464 * @see java.awt.MediaTracker#statusID(int, boolean) 465 * @see java.awt.MediaTracker#LOADING 466 * @see java.awt.MediaTracker#ABORTED 467 * @see java.awt.MediaTracker#ERRORED 468 * @see java.awt.MediaTracker#COMPLETE 469 */ 470 public int statusAll(boolean load) { 471 return statusAll(load, true); 472 } 473 474 private synchronized int statusAll(boolean load, boolean verify) { 475 MediaEntry cur = head; 476 int status = 0; 477 while (cur != null) { 478 status = status | cur.getStatus(load, verify); 479 cur = cur.next; 480 } 481 return status; 482 } 483 484 /** 485 * Checks to see if all images tracked by this media tracker that 486 * are tagged with the specified identifier have finished loading. 487 * <p> 488 * This method does not start loading the images if they are not 489 * already loading. 490 * <p> 491 * If there is an error while loading or scaling an image, then that 492 * image is considered to have finished loading. Use the 493 * <code>isErrorAny</code> or <code>isErrorID</code> methods to 494 * check for errors. 495 * @param id the identifier of the images to check 496 * @return <code>true</code> if all images have finished loading, 497 * have been aborted, or have encountered 498 * an error; <code>false</code> otherwise 499 * @see java.awt.MediaTracker#checkID(int, boolean) 500 * @see java.awt.MediaTracker#checkAll() 501 * @see java.awt.MediaTracker#isErrorAny() 502 * @see java.awt.MediaTracker#isErrorID(int) 503 */ 504 public boolean checkID(int id) { 505 return checkID(id, false, true); 506 } 507 508 /** 509 * Checks to see if all images tracked by this media tracker that 510 * are tagged with the specified identifier have finished loading. 511 * <p> 512 * If the value of the <code>load</code> flag is <code>true</code>, 513 * then this method starts loading any images that are not yet 514 * being loaded. 515 * <p> 516 * If there is an error while loading or scaling an image, then that 517 * image is considered to have finished loading. Use the 518 * <code>isErrorAny</code> or <code>isErrorID</code> methods to 519 * check for errors. 520 * @param id the identifier of the images to check 521 * @param load if <code>true</code>, start loading any 522 * images that are not yet being loaded 523 * @return <code>true</code> if all images have finished loading, 524 * have been aborted, or have encountered 525 * an error; <code>false</code> otherwise 526 * @see java.awt.MediaTracker#checkID(int, boolean) 527 * @see java.awt.MediaTracker#checkAll() 528 * @see java.awt.MediaTracker#isErrorAny() 529 * @see java.awt.MediaTracker#isErrorID(int) 530 */ 531 public boolean checkID(int id, boolean load) { 532 return checkID(id, load, true); 533 } 534 535 private synchronized boolean checkID(int id, boolean load, boolean verify) 536 { 537 MediaEntry cur = head; 538 boolean done = true; 539 while (cur != null) { 540 if (cur.getID() == id 541 && (cur.getStatus(load, verify) & DONE) == 0) 542 { 543 done = false; 544 } 545 cur = cur.next; 546 } 547 return done; 548 } 549 550 /** 551 * Checks the error status of all of the images tracked by this 552 * media tracker with the specified identifier. 553 * @param id the identifier of the images to check 554 * @return <code>true</code> if any of the images with the 555 * specified identifier had an error during 556 * loading; <code>false</code> otherwise 557 * @see java.awt.MediaTracker#isErrorAny 558 * @see java.awt.MediaTracker#getErrorsID 559 */ 560 public synchronized boolean isErrorID(int id) { 561 MediaEntry cur = head; 562 while (cur != null) { 563 if (cur.getID() == id 564 && (cur.getStatus(false, true) & ERRORED) != 0) 565 { 566 return true; 567 } 568 cur = cur.next; 569 } 570 return false; 571 } 572 573 /** 574 * Returns a list of media with the specified ID that 575 * have encountered an error. 576 * @param id the identifier of the images to check 577 * @return an array of media objects tracked by this media 578 * tracker with the specified identifier 579 * that have encountered an error, or 580 * <code>null</code> if there are none with errors 581 * @see java.awt.MediaTracker#isErrorID 582 * @see java.awt.MediaTracker#isErrorAny 583 * @see java.awt.MediaTracker#getErrorsAny 584 */ 585 public synchronized Object[] getErrorsID(int id) { 586 MediaEntry cur = head; 587 int numerrors = 0; 588 while (cur != null) { 589 if (cur.getID() == id 590 && (cur.getStatus(false, true) & ERRORED) != 0) 591 { 592 numerrors++; 593 } 594 cur = cur.next; 595 } 596 if (numerrors == 0) { 597 return null; 598 } 599 Object errors[] = new Object[numerrors]; 600 cur = head; 601 numerrors = 0; 602 while (cur != null) { 603 if (cur.getID() == id 604 && (cur.getStatus(false, false) & ERRORED) != 0) 605 { 606 errors[numerrors++] = cur.getMedia(); 607 } 608 cur = cur.next; 609 } 610 return errors; 611 } 612 613 /** 614 * Starts loading all images tracked by this media tracker with the 615 * specified identifier. This method waits until all the images with 616 * the specified identifier have finished loading. 617 * <p> 618 * If there is an error while loading or scaling an image, then that 619 * image is considered to have finished loading. Use the 620 * <code>isErrorAny</code> and <code>isErrorID</code> methods to 621 * check for errors. 622 * @param id the identifier of the images to check 623 * @see java.awt.MediaTracker#waitForAll 624 * @see java.awt.MediaTracker#isErrorAny() 625 * @see java.awt.MediaTracker#isErrorID(int) 626 * @exception InterruptedException if any thread has 627 * interrupted this thread. 628 */ 629 public void waitForID(int id) throws InterruptedException { 630 waitForID(id, 0); 631 } 632 633 /** 634 * Starts loading all images tracked by this media tracker with the 635 * specified identifier. This method waits until all the images with 636 * the specified identifier have finished loading, or until the 637 * length of time specified in milliseconds by the <code>ms</code> 638 * argument has passed. 639 * <p> 640 * If there is an error while loading or scaling an image, then that 641 * image is considered to have finished loading. Use the 642 * <code>statusID</code>, <code>isErrorID</code>, and 643 * <code>isErrorAny</code> methods to check for errors. 644 * @param id the identifier of the images to check 645 * @param ms the length of time, in milliseconds, to wait 646 * for the loading to complete 647 * @see java.awt.MediaTracker#waitForAll 648 * @see java.awt.MediaTracker#waitForID(int) 649 * @see java.awt.MediaTracker#statusID 650 * @see java.awt.MediaTracker#isErrorAny() 651 * @see java.awt.MediaTracker#isErrorID(int) 652 * @exception InterruptedException if any thread has 653 * interrupted this thread. 654 */ 655 public synchronized boolean waitForID(int id, long ms) 656 throws InterruptedException 657 { 658 long end = System.currentTimeMillis() + ms; 659 boolean first = true; 660 while (true) { 661 int status = statusID(id, first, first); 662 if ((status & LOADING) == 0) { 663 return (status == COMPLETE); 664 } 665 first = false; 666 long timeout; 667 if (ms == 0) { 668 timeout = 0; 669 } else { 670 timeout = end - System.currentTimeMillis(); 671 if (timeout <= 0) { 672 return false; 673 } 674 } 675 wait(timeout); 676 } 677 } 678 679 /** 680 * Calculates and returns the bitwise inclusive <b>OR</b> of the 681 * status of all media with the specified identifier that are 682 * tracked by this media tracker. 683 * <p> 684 * Possible flags defined by the 685 * <code>MediaTracker</code> class are <code>LOADING</code>, 686 * <code>ABORTED</code>, <code>ERRORED</code>, and 687 * <code>COMPLETE</code>. An image that hasn't started 688 * loading has zero as its status. 689 * <p> 690 * If the value of <code>load</code> is <code>true</code>, then 691 * this method starts loading any images that are not yet being loaded. 692 * @param id the identifier of the images to check 693 * @param load if <code>true</code>, start loading 694 * any images that are not yet being loaded 695 * @return the bitwise inclusive <b>OR</b> of the status of 696 * all of the media with the specified 697 * identifier that are being tracked 698 * @see java.awt.MediaTracker#statusAll(boolean) 699 * @see java.awt.MediaTracker#LOADING 700 * @see java.awt.MediaTracker#ABORTED 701 * @see java.awt.MediaTracker#ERRORED 702 * @see java.awt.MediaTracker#COMPLETE 703 */ 704 public int statusID(int id, boolean load) { 705 return statusID(id, load, true); 706 } 707 708 private synchronized int statusID(int id, boolean load, boolean verify) { 709 MediaEntry cur = head; 710 int status = 0; 711 while (cur != null) { 712 if (cur.getID() == id) { 713 status = status | cur.getStatus(load, verify); 714 } 715 cur = cur.next; 716 } 717 return status; 718 } 719 720 /** 721 * Removes the specified image from this media tracker. 722 * All instances of the specified image are removed, 723 * regardless of scale or ID. 724 * @param image the image to be removed 725 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 726 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 727 * @since JDK1.1 728 */ 729 public synchronized void removeImage(Image image) { 730 removeImageImpl(image); 731 Image rvImage = getResolutionVariant(image); 732 if (rvImage != null) { 733 removeImageImpl(rvImage); 734 } 735 notifyAll(); // Notify in case remaining images are "done". 736 } 737 738 private void removeImageImpl(Image image) { 739 MediaEntry cur = head; 740 MediaEntry prev = null; 741 while (cur != null) { 742 MediaEntry next = cur.next; 743 if (cur.getMedia() == image) { 744 if (prev == null) { 745 head = next; 746 } else { 747 prev.next = next; 748 } 749 cur.cancel(); 750 } else { 751 prev = cur; 752 } 753 cur = next; 754 } 755 } 756 757 /** 758 * Removes the specified image from the specified tracking 759 * ID of this media tracker. 760 * All instances of <code>Image</code> being tracked 761 * under the specified ID are removed regardless of scale. 762 * @param image the image to be removed 763 * @param id the tracking ID from which to remove the image 764 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 765 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 766 * @since JDK1.1 767 */ 768 public synchronized void removeImage(Image image, int id) { 769 removeImageImpl(image, id); 770 Image rvImage = getResolutionVariant(image); 771 if (rvImage != null) { 772 removeImageImpl(rvImage, id); 773 } 774 notifyAll(); // Notify in case remaining images are "done". 775 } 776 777 private void removeImageImpl(Image image, int id) { 778 MediaEntry cur = head; 779 MediaEntry prev = null; 780 while (cur != null) { 781 MediaEntry next = cur.next; 782 if (cur.getID() == id && cur.getMedia() == image) { 783 if (prev == null) { 784 head = next; 785 } else { 786 prev.next = next; 787 } 788 cur.cancel(); 789 } else { 790 prev = cur; 791 } 792 cur = next; 793 } 794 } 795 796 /** 797 * Removes the specified image with the specified 798 * width, height, and ID from this media tracker. 799 * Only the specified instance (with any duplicates) is removed. 800 * @param image the image to be removed 801 * @param id the tracking ID from which to remove the image 802 * @param width the width to remove (-1 for unscaled) 803 * @param height the height to remove (-1 for unscaled) 804 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 805 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 806 * @since JDK1.1 807 */ 808 public synchronized void removeImage(Image image, int id, 809 int width, int height) { 810 removeImageImpl(image, id, width, height); 811 Image rvImage = getResolutionVariant(image); 812 if (rvImage != null) { 813 removeImageImpl(rvImage, id, 2 * width, 2 * height); 814 815 } 816 notifyAll(); // Notify in case remaining images are "done". 817 } 818 819 private void removeImageImpl(Image image, int id, int width, int height) { 820 MediaEntry cur = head; 821 MediaEntry prev = null; 822 while (cur != null) { 823 MediaEntry next = cur.next; 824 if (cur.getID() == id && cur instanceof ImageMediaEntry 825 && ((ImageMediaEntry) cur).matches(image, width, height)) 826 { 827 if (prev == null) { 828 head = next; 829 } else { 830 prev.next = next; 831 } 832 cur.cancel(); 833 } else { 834 prev = cur; 835 } 836 cur = next; 837 } 838 } 839 840 synchronized void setDone() { 841 notifyAll(); 842 } 843 844 private static Image getResolutionVariant(Image image) { 845 if (image instanceof MultiResolutionToolkitImage) { 846 return ((MultiResolutionToolkitImage) image).getResolutionVariant(); 847 } 848 return null; 849 } 850 } 851 852 abstract class MediaEntry { 853 MediaTracker tracker; 854 int ID; 855 MediaEntry next; 856 857 int status; 858 boolean cancelled; 859 860 MediaEntry(MediaTracker mt, int id) { 861 tracker = mt; 862 ID = id; 863 } 864 865 abstract Object getMedia(); 866 867 static MediaEntry insert(MediaEntry head, MediaEntry me) { 868 MediaEntry cur = head; 869 MediaEntry prev = null; 870 while (cur != null) { 871 if (cur.ID > me.ID) { 872 break; 873 } 874 prev = cur; 875 cur = cur.next; 876 } 877 me.next = cur; 878 if (prev == null) { 879 head = me; 880 } else { 881 prev.next = me; 882 } 883 return head; 884 } 885 886 int getID() { 887 return ID; 888 } 889 890 abstract void startLoad(); 891 892 void cancel() { 893 cancelled = true; 894 } 895 896 static final int LOADING = MediaTracker.LOADING; 897 static final int ABORTED = MediaTracker.ABORTED; 898 static final int ERRORED = MediaTracker.ERRORED; 899 static final int COMPLETE = MediaTracker.COMPLETE; 900 901 static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE); 902 static final int DONE = (ABORTED | ERRORED | COMPLETE); 903 904 synchronized int getStatus(boolean doLoad, boolean doVerify) { 905 if (doLoad && ((status & LOADSTARTED) == 0)) { 906 status = (status & ~ABORTED) | LOADING; 907 startLoad(); 908 } 909 return status; 910 } 911 912 void setStatus(int flag) { 913 synchronized (this) { 914 status = flag; 915 } 916 tracker.setDone(); 917 } 918 } 919 920 class ImageMediaEntry extends MediaEntry implements ImageObserver, 921 java.io.Serializable { 922 Image image; 923 int width; 924 int height; 925 926 /* 927 * JDK 1.1 serialVersionUID 928 */ 929 private static final long serialVersionUID = 4739377000350280650L; 930 931 ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) { 932 super(mt, c); 933 image = img; 934 width = w; 935 height = h; 936 } 937 938 boolean matches(Image img, int w, int h) { 939 return (image == img && width == w && height == h); 940 } 941 942 Object getMedia() { 943 return image; 944 } 945 946 synchronized int getStatus(boolean doLoad, boolean doVerify) { 947 if (doVerify) { 948 int flags = tracker.target.checkImage(image, width, height, null); 949 int s = parseflags(flags); 950 if (s == 0) { 951 if ((status & (ERRORED | COMPLETE)) != 0) { 952 setStatus(ABORTED); 953 } 954 } else if (s != status) { 955 setStatus(s); 956 } 957 } 958 return super.getStatus(doLoad, doVerify); 959 } 960 961 void startLoad() { 962 if (tracker.target.prepareImage(image, width, height, this)) { 963 setStatus(COMPLETE); 964 } 965 } 966 967 int parseflags(int infoflags) { 968 if ((infoflags & ERROR) != 0) { 969 return ERRORED; 970 } else if ((infoflags & ABORT) != 0) { 971 return ABORTED; 972 } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) { 973 return COMPLETE; 974 } 975 return 0; 976 } 977 978 public boolean imageUpdate(Image img, int infoflags, 979 int x, int y, int w, int h) { 980 if (cancelled) { 981 return false; 982 } 983 int s = parseflags(infoflags); 984 if (s != 0 && s != status) { 985 setStatus(s); 986 } 987 return ((status & LOADING) != 0); 988 } 989 }