1 /* 2 * Copyright (c) 1995, 2014, 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 * 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 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 * @return {@code true} if the loading completed in time; 648 * otherwise {@code false} 649 * @see java.awt.MediaTracker#waitForAll 650 * @see java.awt.MediaTracker#waitForID(int) 651 * @see java.awt.MediaTracker#statusID 652 * @see java.awt.MediaTracker#isErrorAny() 653 * @see java.awt.MediaTracker#isErrorID(int) 654 * @exception InterruptedException if any thread has 655 * interrupted this thread. 656 */ 657 public synchronized boolean waitForID(int id, long ms) 658 throws InterruptedException 659 { 660 long end = System.currentTimeMillis() + ms; 661 boolean first = true; 662 while (true) { 663 int status = statusID(id, first, first); 664 if ((status & LOADING) == 0) { 665 return (status == COMPLETE); 666 } 667 first = false; 668 long timeout; 669 if (ms == 0) { 670 timeout = 0; 671 } else { 672 timeout = end - System.currentTimeMillis(); 673 if (timeout <= 0) { 674 return false; 675 } 676 } 677 wait(timeout); 678 } 679 } 680 681 /** 682 * Calculates and returns the bitwise inclusive <b>OR</b> of the 683 * status of all media with the specified identifier that are 684 * tracked by this media tracker. 685 * <p> 686 * Possible flags defined by the 687 * <code>MediaTracker</code> class are <code>LOADING</code>, 688 * <code>ABORTED</code>, <code>ERRORED</code>, and 689 * <code>COMPLETE</code>. An image that hasn't started 690 * loading has zero as its status. 691 * <p> 692 * If the value of <code>load</code> is <code>true</code>, then 693 * this method starts loading any images that are not yet being loaded. 694 * @param id the identifier of the images to check 695 * @param load if <code>true</code>, start loading 696 * any images that are not yet being loaded 697 * @return the bitwise inclusive <b>OR</b> of the status of 698 * all of the media with the specified 699 * identifier that are being tracked 700 * @see java.awt.MediaTracker#statusAll(boolean) 701 * @see java.awt.MediaTracker#LOADING 702 * @see java.awt.MediaTracker#ABORTED 703 * @see java.awt.MediaTracker#ERRORED 704 * @see java.awt.MediaTracker#COMPLETE 705 */ 706 public int statusID(int id, boolean load) { 707 return statusID(id, load, true); 708 } 709 710 private synchronized int statusID(int id, boolean load, boolean verify) { 711 MediaEntry cur = head; 712 int status = 0; 713 while (cur != null) { 714 if (cur.getID() == id) { 715 status = status | cur.getStatus(load, verify); 716 } 717 cur = cur.next; 718 } 719 return status; 720 } 721 722 /** 723 * Removes the specified image from this media tracker. 724 * All instances of the specified image are removed, 725 * regardless of scale or ID. 726 * @param image the image to be removed 727 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 728 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 729 * @since JDK1.1 730 */ 731 public synchronized void removeImage(Image image) { 732 removeImageImpl(image); 733 Image rvImage = getResolutionVariant(image); 734 if (rvImage != null) { 735 removeImageImpl(rvImage); 736 } 737 notifyAll(); // Notify in case remaining images are "done". 738 } 739 740 private void removeImageImpl(Image image) { 741 MediaEntry cur = head; 742 MediaEntry prev = null; 743 while (cur != null) { 744 MediaEntry next = cur.next; 745 if (cur.getMedia() == image) { 746 if (prev == null) { 747 head = next; 748 } else { 749 prev.next = next; 750 } 751 cur.cancel(); 752 } else { 753 prev = cur; 754 } 755 cur = next; 756 } 757 } 758 759 /** 760 * Removes the specified image from the specified tracking 761 * ID of this media tracker. 762 * All instances of <code>Image</code> being tracked 763 * under the specified ID are removed regardless of scale. 764 * @param image the image to be removed 765 * @param id the tracking ID from which to remove the image 766 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 767 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int) 768 * @since JDK1.1 769 */ 770 public synchronized void removeImage(Image image, int id) { 771 removeImageImpl(image, id); 772 Image rvImage = getResolutionVariant(image); 773 if (rvImage != null) { 774 removeImageImpl(rvImage, id); 775 } 776 notifyAll(); // Notify in case remaining images are "done". 777 } 778 779 private void removeImageImpl(Image image, int id) { 780 MediaEntry cur = head; 781 MediaEntry prev = null; 782 while (cur != null) { 783 MediaEntry next = cur.next; 784 if (cur.getID() == id && cur.getMedia() == image) { 785 if (prev == null) { 786 head = next; 787 } else { 788 prev.next = next; 789 } 790 cur.cancel(); 791 } else { 792 prev = cur; 793 } 794 cur = next; 795 } 796 } 797 798 /** 799 * Removes the specified image with the specified 800 * width, height, and ID from this media tracker. 801 * Only the specified instance (with any duplicates) is removed. 802 * @param image the image to be removed 803 * @param id the tracking ID from which to remove the image 804 * @param width the width to remove (-1 for unscaled) 805 * @param height the height to remove (-1 for unscaled) 806 * @see java.awt.MediaTracker#removeImage(java.awt.Image) 807 * @see java.awt.MediaTracker#removeImage(java.awt.Image, int) 808 * @since JDK1.1 809 */ 810 public synchronized void removeImage(Image image, int id, 811 int width, int height) { 812 removeImageImpl(image, id, width, height); 813 Image rvImage = getResolutionVariant(image); 814 if (rvImage != null) { 815 removeImageImpl(rvImage, id, 2 * width, 2 * height); 816 817 } 818 notifyAll(); // Notify in case remaining images are "done". 819 } 820 821 private void removeImageImpl(Image image, int id, int width, int height) { 822 MediaEntry cur = head; 823 MediaEntry prev = null; 824 while (cur != null) { 825 MediaEntry next = cur.next; 826 if (cur.getID() == id && cur instanceof ImageMediaEntry 827 && ((ImageMediaEntry) cur).matches(image, width, height)) 828 { 829 if (prev == null) { 830 head = next; 831 } else { 832 prev.next = next; 833 } 834 cur.cancel(); 835 } else { 836 prev = cur; 837 } 838 cur = next; 839 } 840 } 841 842 synchronized void setDone() { 843 notifyAll(); 844 } 845 846 private static Image getResolutionVariant(Image image) { 847 if (image instanceof MultiResolutionToolkitImage) { 848 return ((MultiResolutionToolkitImage) image).getResolutionVariant(); 849 } 850 return null; 851 } 852 } 853 854 abstract class MediaEntry { 855 MediaTracker tracker; 856 int ID; 857 MediaEntry next; 858 859 int status; 860 boolean cancelled; 861 862 MediaEntry(MediaTracker mt, int id) { 863 tracker = mt; 864 ID = id; 865 } 866 867 abstract Object getMedia(); 868 869 static MediaEntry insert(MediaEntry head, MediaEntry me) { 870 MediaEntry cur = head; 871 MediaEntry prev = null; 872 while (cur != null) { 873 if (cur.ID > me.ID) { 874 break; 875 } 876 prev = cur; 877 cur = cur.next; 878 } 879 me.next = cur; 880 if (prev == null) { 881 head = me; 882 } else { 883 prev.next = me; 884 } 885 return head; 886 } 887 888 int getID() { 889 return ID; 890 } 891 892 abstract void startLoad(); 893 894 void cancel() { 895 cancelled = true; 896 } 897 898 static final int LOADING = MediaTracker.LOADING; 899 static final int ABORTED = MediaTracker.ABORTED; 900 static final int ERRORED = MediaTracker.ERRORED; 901 static final int COMPLETE = MediaTracker.COMPLETE; 902 903 static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE); 904 static final int DONE = (ABORTED | ERRORED | COMPLETE); 905 906 synchronized int getStatus(boolean doLoad, boolean doVerify) { 907 if (doLoad && ((status & LOADSTARTED) == 0)) { 908 status = (status & ~ABORTED) | LOADING; 909 startLoad(); 910 } 911 return status; 912 } 913 914 void setStatus(int flag) { 915 synchronized (this) { 916 status = flag; 917 } 918 tracker.setDone(); 919 } 920 } 921 922 class ImageMediaEntry extends MediaEntry implements ImageObserver, 923 java.io.Serializable { 924 Image image; 925 int width; 926 int height; 927 928 /* 929 * JDK 1.1 serialVersionUID 930 */ 931 private static final long serialVersionUID = 4739377000350280650L; 932 933 ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) { 934 super(mt, c); 935 image = img; 936 width = w; 937 height = h; 938 } 939 940 boolean matches(Image img, int w, int h) { 941 return (image == img && width == w && height == h); 942 } 943 944 Object getMedia() { 945 return image; 946 } 947 948 synchronized int getStatus(boolean doLoad, boolean doVerify) { 949 if (doVerify) { 950 int flags = tracker.target.checkImage(image, width, height, null); 951 int s = parseflags(flags); 952 if (s == 0) { 953 if ((status & (ERRORED | COMPLETE)) != 0) { 954 setStatus(ABORTED); 955 } 956 } else if (s != status) { 957 setStatus(s); 958 } 959 } 960 return super.getStatus(doLoad, doVerify); 961 } 962 963 void startLoad() { 964 if (tracker.target.prepareImage(image, width, height, this)) { 965 setStatus(COMPLETE); 966 } 967 } 968 969 int parseflags(int infoflags) { 970 if ((infoflags & ERROR) != 0) { 971 return ERRORED; 972 } else if ((infoflags & ABORT) != 0) { 973 return ABORTED; 974 } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) { 975 return COMPLETE; 976 } 977 return 0; 978 } 979 980 public boolean imageUpdate(Image img, int infoflags, 981 int x, int y, int w, int h) { 982 if (cancelled) { 983 return false; 984 } 985 int s = parseflags(infoflags); 986 if (s != 0 && s != status) { 987 setStatus(s); 988 } 989 return ((status & LOADING) != 0); 990 } 991 }