1 /* 2 * Copyright (c) 1995, 2018, 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} 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} and call its {@code addImage} 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} treats an animated image 56 * as completely loaded 57 * when the first frame is completely loaded. 58 * At that point, the {@code MediaTracker} 59 * signals any waiters 60 * that the image is completely loaded. 61 * If no {@code ImageObserver}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}: 69 * 70 * <hr><blockquote><pre>{@code 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 1.0 168 */ 169 public class MediaTracker implements java.io.Serializable { 170 171 /** 172 * A given {@code Component} 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} that is being 182 * tracked by the {@code MediaTracker}. 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, 230 w == -1 ? -1 : 2 * w, 231 h == -1 ? -1 : 2 * h); 232 } 233 } 234 235 private void addImageImpl(Image image, int id, int w, int h) { 236 head = MediaEntry.insert(head, 237 new ImageMediaEntry(this, image, id, w, h)); 238 } 239 /** 240 * Flag indicating that media is currently being loaded. 241 * @see java.awt.MediaTracker#statusAll 242 * @see java.awt.MediaTracker#statusID 243 */ 244 public static final int LOADING = 1; 245 246 /** 247 * Flag indicating that the downloading of media was aborted. 248 * @see java.awt.MediaTracker#statusAll 249 * @see java.awt.MediaTracker#statusID 250 */ 251 public static final int ABORTED = 2; 252 253 /** 254 * Flag indicating that the downloading of media encountered 255 * an error. 256 * @see java.awt.MediaTracker#statusAll 257 * @see java.awt.MediaTracker#statusID 258 */ 259 public static final int ERRORED = 4; 260 261 /** 262 * Flag indicating that the downloading of media was completed 263 * successfully. 264 * @see java.awt.MediaTracker#statusAll 265 * @see java.awt.MediaTracker#statusID 266 */ 267 public static final int COMPLETE = 8; 268 269 static final int DONE = (ABORTED | ERRORED | COMPLETE); 270 271 /** 272 * Checks to see if all images being tracked by this media tracker 273 * have finished loading. 274 * <p> 275 * This method does not start loading the images if they are not 276 * already loading. 277 * <p> 278 * If there is an error while loading or scaling an image, then that 279 * image is considered to have finished loading. Use the 280 * {@code isErrorAny} or {@code isErrorID} methods to 281 * check for errors. 282 * @return {@code true} if all images have finished loading, 283 * have been aborted, or have encountered 284 * an error; {@code false} otherwise 285 * @see java.awt.MediaTracker#checkAll(boolean) 286 * @see java.awt.MediaTracker#checkID 287 * @see java.awt.MediaTracker#isErrorAny 288 * @see java.awt.MediaTracker#isErrorID 289 */ 290 public boolean checkAll() { 291 return checkAll(false, true); 292 } 293 294 /** 295 * Checks to see if all images being tracked by this media tracker 296 * have finished loading. 297 * <p> 298 * If the value of the {@code load} flag is {@code true}, 299 * then this method starts loading any images that are not yet 300 * being loaded. 301 * <p> 302 * If there is an error while loading or scaling an image, that 303 * image is considered to have finished loading. Use the 304 * {@code isErrorAny} and {@code isErrorID} methods to 305 * check for errors. 306 * @param load if {@code true}, start loading any 307 * images that are not yet being loaded 308 * @return {@code true} if all images have finished loading, 309 * have been aborted, or have encountered 310 * an error; {@code false} otherwise 311 * @see java.awt.MediaTracker#checkID 312 * @see java.awt.MediaTracker#checkAll() 313 * @see java.awt.MediaTracker#isErrorAny() 314 * @see java.awt.MediaTracker#isErrorID(int) 315 */ 316 public boolean checkAll(boolean load) { 317 return checkAll(load, true); 318 } 319 320 private synchronized boolean checkAll(boolean load, boolean verify) { 321 MediaEntry cur = head; 322 boolean done = true; 323 while (cur != null) { 324 if ((cur.getStatus(load, verify) & DONE) == 0) { 325 done = false; 326 } 327 cur = cur.next; 328 } 329 return done; 330 } 331 332 /** 333 * Checks the error status of all of the images. 334 * @return {@code true} if any of the images tracked 335 * by this media tracker had an error during 336 * loading; {@code false} otherwise 337 * @see java.awt.MediaTracker#isErrorID 338 * @see java.awt.MediaTracker#getErrorsAny 339 */ 340 public synchronized boolean isErrorAny() { 341 MediaEntry cur = head; 342 while (cur != null) { 343 if ((cur.getStatus(false, true) & ERRORED) != 0) { 344 return true; 345 } 346 cur = cur.next; 347 } 348 return false; 349 } 350 351 /** 352 * Returns a list of all media that have encountered an error. 353 * @return an array of media objects tracked by this 354 * media tracker that have encountered 355 * an error, or {@code null} if 356 * there are none with errors 357 * @see java.awt.MediaTracker#isErrorAny 358 * @see java.awt.MediaTracker#getErrorsID 359 */ 360 public synchronized Object[] getErrorsAny() { 361 MediaEntry cur = head; 362 int numerrors = 0; 363 while (cur != null) { 364 if ((cur.getStatus(false, true) & ERRORED) != 0) { 365 numerrors++; 366 } 367 cur = cur.next; 368 } 369 if (numerrors == 0) { 370 return null; 371 } 372 Object[] errors = new Object[numerrors]; 373 cur = head; 374 numerrors = 0; 375 while (cur != null) { 376 if ((cur.getStatus(false, false) & ERRORED) != 0) { 377 errors[numerrors++] = cur.getMedia(); 378 } 379 cur = cur.next; 380 } 381 return errors; 382 } 383 384 /** 385 * Starts loading all images tracked by this media tracker. This 386 * method waits until all the images being tracked have finished 387 * loading. 388 * <p> 389 * If there is an error while loading or scaling an image, then that 390 * image is considered to have finished loading. Use the 391 * {@code isErrorAny} or {@code isErrorID} methods to 392 * check for errors. 393 * @see java.awt.MediaTracker#waitForID(int) 394 * @see java.awt.MediaTracker#waitForAll(long) 395 * @see java.awt.MediaTracker#isErrorAny 396 * @see java.awt.MediaTracker#isErrorID 397 * @exception InterruptedException if any thread has 398 * interrupted this thread 399 */ 400 public void waitForAll() throws InterruptedException { 401 waitForAll(0); 402 } 403 404 /** 405 * Starts loading all images tracked by this media tracker. This 406 * method waits until all the images being tracked have finished 407 * loading, or until the length of time specified in milliseconds 408 * by the {@code ms} argument has passed. 409 * <p> 410 * If there is an error while loading or scaling an image, then 411 * that image is considered to have finished loading. Use the 412 * {@code isErrorAny} or {@code isErrorID} methods to 413 * check for errors. 414 * @param ms the number of milliseconds to wait 415 * for the loading to complete 416 * @return {@code true} if all images were successfully 417 * loaded; {@code false} otherwise 418 * @see java.awt.MediaTracker#waitForID(int) 419 * @see java.awt.MediaTracker#waitForAll(long) 420 * @see java.awt.MediaTracker#isErrorAny 421 * @see java.awt.MediaTracker#isErrorID 422 * @exception InterruptedException if any thread has 423 * interrupted this thread. 424 */ 425 public synchronized boolean waitForAll(long ms) 426 throws InterruptedException 427 { 428 long end = System.currentTimeMillis() + ms; 429 boolean first = true; 430 while (true) { 431 int status = statusAll(first, first); 432 if ((status & LOADING) == 0) { 433 return (status == COMPLETE); 434 } 435 first = false; 436 long timeout; 437 if (ms == 0) { 438 timeout = 0; 439 } else { 440 timeout = end - System.currentTimeMillis(); 441 if (timeout <= 0) { 442 return false; 443 } 444 } 445 wait(timeout); 446 } 447 } 448 449 /** 450 * Calculates and returns the bitwise inclusive <b>OR</b> of the 451 * status of all media that are tracked by this media tracker. 452 * <p> 453 * Possible flags defined by the 454 * {@code MediaTracker} class are {@code LOADING}, 455 * {@code ABORTED}, {@code ERRORED}, and 456 * {@code COMPLETE}. An image that hasn't started 457 * loading has zero as its status. 458 * <p> 459 * If the value of {@code load} is {@code true}, then 460 * this method starts loading any images that are not yet being loaded. 461 * 462 * @param load if {@code true}, start loading 463 * any images that are not yet being loaded 464 * @return the bitwise inclusive <b>OR</b> of the status of 465 * all of the media being tracked 466 * @see java.awt.MediaTracker#statusID(int, boolean) 467 * @see java.awt.MediaTracker#LOADING 468 * @see java.awt.MediaTracker#ABORTED 469 * @see java.awt.MediaTracker#ERRORED 470 * @see java.awt.MediaTracker#COMPLETE 471 */ 472 public int statusAll(boolean load) { 473 return statusAll(load, true); 474 } 475 476 private synchronized int statusAll(boolean load, boolean verify) { 477 MediaEntry cur = head; 478 int status = 0; 479 while (cur != null) { 480 status = status | cur.getStatus(load, verify); 481 cur = cur.next; 482 } 483 return status; 484 } 485 486 /** 487 * Checks to see if all images tracked by this media tracker that 488 * are tagged with the specified identifier have finished loading. 489 * <p> 490 * This method does not start loading the images if they are not 491 * already loading. 492 * <p> 493 * If there is an error while loading or scaling an image, then that 494 * image is considered to have finished loading. Use the 495 * {@code isErrorAny} or {@code isErrorID} methods to 496 * check for errors. 497 * @param id the identifier of the images to check 498 * @return {@code true} if all images have finished loading, 499 * have been aborted, or have encountered 500 * an error; {@code false} otherwise 501 * @see java.awt.MediaTracker#checkID(int, boolean) 502 * @see java.awt.MediaTracker#checkAll() 503 * @see java.awt.MediaTracker#isErrorAny() 504 * @see java.awt.MediaTracker#isErrorID(int) 505 */ 506 public boolean checkID(int id) { 507 return checkID(id, false, true); 508 } 509 510 /** 511 * Checks to see if all images tracked by this media tracker that 512 * are tagged with the specified identifier have finished loading. 513 * <p> 514 * If the value of the {@code load} flag is {@code true}, 515 * then this method starts loading any images that are not yet 516 * being loaded. 517 * <p> 518 * If there is an error while loading or scaling an image, then that 519 * image is considered to have finished loading. Use the 520 * {@code isErrorAny} or {@code isErrorID} methods to 521 * check for errors. 522 * @param id the identifier of the images to check 523 * @param load if {@code true}, start loading any 524 * images that are not yet being loaded 525 * @return {@code true} if all images have finished loading, 526 * have been aborted, or have encountered 527 * an error; {@code false} otherwise 528 * @see java.awt.MediaTracker#checkID(int, boolean) 529 * @see java.awt.MediaTracker#checkAll() 530 * @see java.awt.MediaTracker#isErrorAny() 531 * @see java.awt.MediaTracker#isErrorID(int) 532 */ 533 public boolean checkID(int id, boolean load) { 534 return checkID(id, load, true); 535 } 536 537 private synchronized boolean checkID(int id, boolean load, boolean verify) 538 { 539 MediaEntry cur = head; 540 boolean done = true; 541 while (cur != null) { 542 if (cur.getID() == id 543 && (cur.getStatus(load, verify) & DONE) == 0) 544 { 545 done = false; 546 } 547 cur = cur.next; 548 } 549 return done; 550 } 551 552 /** 553 * Checks the error status of all of the images tracked by this 554 * media tracker with the specified identifier. 555 * @param id the identifier of the images to check 556 * @return {@code true} if any of the images with the 557 * specified identifier had an error during 558 * loading; {@code false} otherwise 559 * @see java.awt.MediaTracker#isErrorAny 560 * @see java.awt.MediaTracker#getErrorsID 561 */ 562 public synchronized boolean isErrorID(int id) { 563 MediaEntry cur = head; 564 while (cur != null) { 565 if (cur.getID() == id 566 && (cur.getStatus(false, true) & ERRORED) != 0) 567 { 568 return true; 569 } 570 cur = cur.next; 571 } 572 return false; 573 } 574 575 /** 576 * Returns a list of media with the specified ID that 577 * have encountered an error. 578 * @param id the identifier of the images to check 579 * @return an array of media objects tracked by this media 580 * tracker with the specified identifier 581 * that have encountered an error, or 582 * {@code null} if there are none with errors 583 * @see java.awt.MediaTracker#isErrorID 584 * @see java.awt.MediaTracker#isErrorAny 585 * @see java.awt.MediaTracker#getErrorsAny 586 */ 587 public synchronized Object[] getErrorsID(int id) { 588 MediaEntry cur = head; 589 int numerrors = 0; 590 while (cur != null) { 591 if (cur.getID() == id 592 && (cur.getStatus(false, true) & ERRORED) != 0) 593 { 594 numerrors++; 595 } 596 cur = cur.next; 597 } 598 if (numerrors == 0) { 599 return null; 600 } 601 Object[] errors = new Object[numerrors]; 602 cur = head; 603 numerrors = 0; 604 while (cur != null) { 605 if (cur.getID() == id 606 && (cur.getStatus(false, false) & ERRORED) != 0) 607 { 608 errors[numerrors++] = cur.getMedia(); 609 } 610 cur = cur.next; 611 } 612 return errors; 613 } 614 615 /** 616 * Starts loading all images tracked by this media tracker with the 617 * specified identifier. This method waits until all the images with 618 * the specified identifier have finished loading. 619 * <p> 620 * If there is an error while loading or scaling an image, then that 621 * image is considered to have finished loading. Use the 622 * {@code isErrorAny} and {@code isErrorID} methods to 623 * check for errors. 624 * @param id the identifier of the images to check 625 * @see java.awt.MediaTracker#waitForAll 626 * @see java.awt.MediaTracker#isErrorAny() 627 * @see java.awt.MediaTracker#isErrorID(int) 628 * @exception InterruptedException if any thread has 629 * interrupted this thread. 630 */ 631 public void waitForID(int id) throws InterruptedException { 632 waitForID(id, 0); 633 } 634 635 /** 636 * Starts loading all images tracked by this media tracker with the 637 * specified identifier. This method waits until all the images with 638 * the specified identifier have finished loading, or until the 639 * length of time specified in milliseconds by the {@code ms} 640 * argument has passed. 641 * <p> 642 * If there is an error while loading or scaling an image, then that 643 * image is considered to have finished loading. Use the 644 * {@code statusID}, {@code isErrorID}, and 645 * {@code isErrorAny} methods to check for errors. 646 * @param id the identifier of the images to check 647 * @param ms the length of time, in milliseconds, to wait 648 * for the loading to complete 649 * @return {@code true} if the loading completed in time; 650 * otherwise {@code false} 651 * @see java.awt.MediaTracker#waitForAll 652 * @see java.awt.MediaTracker#waitForID(int) 653 * @see java.awt.MediaTracker#statusID 654 * @see java.awt.MediaTracker#isErrorAny() 655 * @see java.awt.MediaTracker#isErrorID(int) 656 * @exception InterruptedException if any thread has 657 * interrupted this thread. 658 */ 659 public synchronized boolean waitForID(int id, long ms) 660 throws InterruptedException 661 { 662 long end = System.currentTimeMillis() + ms; 663 boolean first = true; 664 while (true) { 665 int status = statusID(id, first, first); 666 if ((status & LOADING) == 0) { 667 return (status == COMPLETE); 668 } 669 first = false; 670 long timeout; 671 if (ms == 0) { 672 timeout = 0; 673 } else { 674 timeout = end - System.currentTimeMillis(); 675 if (timeout <= 0) { 676 return false; 677 } 678 } 679 wait(timeout); 680 } 681 } 682 683 /** 684 * Calculates and returns the bitwise inclusive <b>OR</b> of the 685 * status of all media with the specified identifier that are 686 * tracked by this media tracker. 687 * <p> 688 * Possible flags defined by the 689 * {@code MediaTracker} class are {@code LOADING}, 690 * {@code ABORTED}, {@code ERRORED}, and 691 * {@code COMPLETE}. An image that hasn't started 692 * loading has zero as its status. 693 * <p> 694 * If the value of {@code load} is {@code true}, then 695 * this method starts loading any images that are not yet being loaded. 696 * @param id the identifier of the images to check 697 * @param load if {@code true}, start loading 698 * any images that are not yet being loaded 699 * @return the bitwise inclusive <b>OR</b> of the status of 700 * all of the media with the specified 701 * identifier that are being tracked 702 * @see java.awt.MediaTracker#statusAll(boolean) 703 * @see java.awt.MediaTracker#LOADING 704 * @see java.awt.MediaTracker#ABORTED 705 * @see java.awt.MediaTracker#ERRORED 706 * @see java.awt.MediaTracker#COMPLETE 707 */ 708 public int statusID(int id, boolean load) { 709 return statusID(id, load, true); 710 } 711 712 private synchronized int statusID(int id, boolean load, boolean verify) { 713 MediaEntry cur = head; 714 int status = 0; 715 while (cur != null) { 716 if (cur.getID() == id) { 717 status = status | cur.getStatus(load, verify); 718 } 719 cur = cur.next; 720 } 721 return status; 722 } 723 724 /** 725 * Removes the specified image from this media tracker. 726 * All instances of the specified image are removed, 727 * regardless of scale or ID. 728 * @param image the image to be removed 729 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 730 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 731 * @since 1.1 732 */ 733 public synchronized void removeImage(Image image) { 734 removeImageImpl(image); 735 Image rvImage = getResolutionVariant(image); 736 if (rvImage != null) { 737 removeImageImpl(rvImage); 738 } 739 notifyAll(); // Notify in case remaining images are "done". 740 } 741 742 private void removeImageImpl(Image image) { 743 MediaEntry cur = head; 744 MediaEntry prev = null; 745 while (cur != null) { 746 MediaEntry next = cur.next; 747 if (cur.getMedia() == image) { 748 if (prev == null) { 749 head = next; 750 } else { 751 prev.next = next; 752 } 753 cur.cancel(); 754 } else { 755 prev = cur; 756 } 757 cur = next; 758 } 759 } 760 761 /** 762 * Removes the specified image from the specified tracking 763 * ID of this media tracker. 764 * All instances of {@code Image} being tracked 765 * under the specified ID are removed regardless of scale. 766 * @param image the image to be removed 767 * @param id the tracking ID from which to remove the image 768 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 769 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 770 * @since 1.1 771 */ 772 public synchronized void removeImage(Image image, int id) { 773 removeImageImpl(image, id); 774 Image rvImage = getResolutionVariant(image); 775 if (rvImage != null) { 776 removeImageImpl(rvImage, id); 777 } 778 notifyAll(); // Notify in case remaining images are "done". 779 } 780 781 private void removeImageImpl(Image image, int id) { 782 MediaEntry cur = head; 783 MediaEntry prev = null; 784 while (cur != null) { 785 MediaEntry next = cur.next; 786 if (cur.getID() == id && cur.getMedia() == image) { 787 if (prev == null) { 788 head = next; 789 } else { 790 prev.next = next; 791 } 792 cur.cancel(); 793 } else { 794 prev = cur; 795 } 796 cur = next; 797 } 798 } 799 800 /** 801 * Removes the specified image with the specified 802 * width, height, and ID from this media tracker. 803 * Only the specified instance (with any duplicates) is removed. 804 * @param image the image to be removed 805 * @param id the tracking ID from which to remove the image 806 * @param width the width to remove (-1 for unscaled) 807 * @param height the height to remove (-1 for unscaled) 808 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 809 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 810 * @since 1.1 811 */ 812 public synchronized void removeImage(Image image, int id, 813 int width, int height) { 814 removeImageImpl(image, id, width, height); 815 Image rvImage = getResolutionVariant(image); 816 if (rvImage != null) { 817 removeImageImpl(rvImage, id, 818 width == -1 ? -1 : 2 * width, 819 height == -1 ? -1 : 2 * height); 820 } 821 notifyAll(); // Notify in case remaining images are "done". 822 } 823 824 private void removeImageImpl(Image image, int id, int width, int height) { 825 MediaEntry cur = head; 826 MediaEntry prev = null; 827 while (cur != null) { 828 MediaEntry next = cur.next; 829 if (cur.getID() == id && cur instanceof ImageMediaEntry 830 && ((ImageMediaEntry) cur).matches(image, width, height)) 831 { 832 if (prev == null) { 833 head = next; 834 } else { 835 prev.next = next; 836 } 837 cur.cancel(); 838 } else { 839 prev = cur; 840 } 841 cur = next; 842 } 843 } 844 845 synchronized void setDone() { 846 notifyAll(); 847 } 848 849 private static Image getResolutionVariant(Image image) { 850 if (image instanceof MultiResolutionToolkitImage) { 851 return ((MultiResolutionToolkitImage) image).getResolutionVariant(); 852 } 853 return null; 854 } 855 } 856 857 abstract class MediaEntry { 858 MediaTracker tracker; 859 int ID; 860 MediaEntry next; 861 862 int status; 863 boolean cancelled; 864 865 MediaEntry(MediaTracker mt, int id) { 866 tracker = mt; 867 ID = id; 868 } 869 870 abstract Object getMedia(); 871 872 static MediaEntry insert(MediaEntry head, MediaEntry me) { 873 MediaEntry cur = head; 874 MediaEntry prev = null; 875 while (cur != null) { 876 if (cur.ID > me.ID) { 877 break; 878 } 879 prev = cur; 880 cur = cur.next; 881 } 882 me.next = cur; 883 if (prev == null) { 884 head = me; 885 } else { 886 prev.next = me; 887 } 888 return head; 889 } 890 891 int getID() { 892 return ID; 893 } 894 895 abstract void startLoad(); 896 897 void cancel() { 898 cancelled = true; 899 } 900 901 static final int LOADING = MediaTracker.LOADING; 902 static final int ABORTED = MediaTracker.ABORTED; 903 static final int ERRORED = MediaTracker.ERRORED; 904 static final int COMPLETE = MediaTracker.COMPLETE; 905 906 static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE); 907 static final int DONE = (ABORTED | ERRORED | COMPLETE); 908 909 synchronized int getStatus(boolean doLoad, boolean doVerify) { 910 if (doLoad && ((status & LOADSTARTED) == 0)) { 911 status = (status & ~ABORTED) | LOADING; 912 startLoad(); 913 } 914 return status; 915 } 916 917 void setStatus(int flag) { 918 synchronized (this) { 919 status = flag; 920 } 921 tracker.setDone(); 922 } 923 } 924 925 class ImageMediaEntry extends MediaEntry implements ImageObserver, 926 java.io.Serializable { 927 Image image; 928 int width; 929 int height; 930 931 /* 932 * JDK 1.1 serialVersionUID 933 */ 934 private static final long serialVersionUID = 4739377000350280650L; 935 936 ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) { 937 super(mt, c); 938 image = img; 939 width = w; 940 height = h; 941 } 942 943 boolean matches(Image img, int w, int h) { 944 return (image == img && width == w && height == h); 945 } 946 947 Object getMedia() { 948 return image; 949 } 950 951 synchronized int getStatus(boolean doLoad, boolean doVerify) { 952 if (doVerify) { 953 int flags = tracker.target.checkImage(image, width, height, null); 954 int s = parseflags(flags); 955 if (s == 0) { 956 if ((status & (ERRORED | COMPLETE)) != 0) { 957 setStatus(ABORTED); 958 } 959 } else if (s != status) { 960 setStatus(s); 961 } 962 } 963 return super.getStatus(doLoad, doVerify); 964 } 965 966 void startLoad() { 967 if (tracker.target.prepareImage(image, width, height, this)) { 968 setStatus(COMPLETE); 969 } 970 } 971 972 int parseflags(int infoflags) { 973 if ((infoflags & ERROR) != 0) { 974 return ERRORED; 975 } else if ((infoflags & ABORT) != 0) { 976 return ABORTED; 977 } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) { 978 return COMPLETE; 979 } 980 return 0; 981 } 982 983 public boolean imageUpdate(Image img, int infoflags, 984 int x, int y, int w, int h) { 985 if (cancelled) { 986 return false; 987 } 988 int s = parseflags(infoflags); 989 if (s != 0 && s != status) { 990 setStatus(s); 991 } 992 return ((status & LOADING) != 0); 993 } 994 }