1 /* 2 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.awt.Component; 29 import java.awt.Image; 30 import java.awt.image.ImageObserver; 31 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, 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</code> or <code>isErrorID</code> methods to 281 * check for errors. 282 * @return <code>true</code> if all images have finished loading, 283 * have been aborted, or have encountered 284 * an error; <code>false</code> 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</code> flag is <code>true</code>, 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</code> and <code>isErrorID</code> methods to 305 * check for errors. 306 * @param load if <code>true</code>, start loading any 307 * images that are not yet being loaded 308 * @return <code>true</code> if all images have finished loading, 309 * have been aborted, or have encountered 310 * an error; <code>false</code> 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</code> if any of the images tracked 335 * by this media tracker had an error during 336 * loading; <code>false</code> 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</code> 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</code> or <code>isErrorID</code> 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</code> 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</code> or <code>isErrorID</code> 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</code> if all images were successfully 417 * loaded; <code>false</code> 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</code> class are <code>LOADING</code>, 455 * <code>ABORTED</code>, <code>ERRORED</code>, and 456 * <code>COMPLETE</code>. An image that hasn't started 457 * loading has zero as its status. 458 * <p> 459 * If the value of <code>load</code> is <code>true</code>, then 460 * this method starts loading any images that are not yet being loaded. 461 * 462 * @param load if <code>true</code>, 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</code> or <code>isErrorID</code> methods to 496 * check for errors. 497 * @param id the identifier of the images to check 498 * @return <code>true</code> if all images have finished loading, 499 * have been aborted, or have encountered 500 * an error; <code>false</code> 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</code> flag is <code>true</code>, 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</code> or <code>isErrorID</code> methods to 521 * check for errors. 522 * @param id the identifier of the images to check 523 * @param load if <code>true</code>, start loading any 524 * images that are not yet being loaded 525 * @return <code>true</code> if all images have finished loading, 526 * have been aborted, or have encountered 527 * an error; <code>false</code> 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</code> if any of the images with the 557 * specified identifier had an error during 558 * loading; <code>false</code> 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</code> 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</code> and <code>isErrorID</code> 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</code> 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>, <code>isErrorID</code>, and 645 * <code>isErrorAny</code> 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 * @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, 816 width == -1 ? -1 : 2 * width, 817 height == -1 ? -1 : 2 * height); 818 } 819 notifyAll(); // Notify in case remaining images are "done". 820 } 821 822 private void removeImageImpl(Image image, int id, int width, int height) { 823 MediaEntry cur = head; 824 MediaEntry prev = null; 825 while (cur != null) { 826 MediaEntry next = cur.next; 827 if (cur.getID() == id && cur instanceof ImageMediaEntry 828 && ((ImageMediaEntry) cur).matches(image, width, height)) 829 { 830 if (prev == null) { 831 head = next; 832 } else { 833 prev.next = next; 834 } 835 cur.cancel(); 836 } else { 837 prev = cur; 838 } 839 cur = next; 840 } 841 } 842 843 synchronized void setDone() { 844 notifyAll(); 845 } 846 847 private static Image getResolutionVariant(Image image) { 848 if (image instanceof MultiResolutionToolkitImage) { 849 return ((MultiResolutionToolkitImage) image).getResolutionVariant(); 850 } 851 return null; 852 } 853 } 854 855 abstract class MediaEntry { 856 MediaTracker tracker; 857 int ID; 858 MediaEntry next; 859 860 int status; 861 boolean cancelled; 862 863 MediaEntry(MediaTracker mt, int id) { 864 tracker = mt; 865 ID = id; 866 } 867 868 abstract Object getMedia(); 869 870 static MediaEntry insert(MediaEntry head, MediaEntry me) { 871 MediaEntry cur = head; 872 MediaEntry prev = null; 873 while (cur != null) { 874 if (cur.ID > me.ID) { 875 break; 876 } 877 prev = cur; 878 cur = cur.next; 879 } 880 me.next = cur; 881 if (prev == null) { 882 head = me; 883 } else { 884 prev.next = me; 885 } 886 return head; 887 } 888 889 int getID() { 890 return ID; 891 } 892 893 abstract void startLoad(); 894 895 void cancel() { 896 cancelled = true; 897 } 898 899 static final int LOADING = MediaTracker.LOADING; 900 static final int ABORTED = MediaTracker.ABORTED; 901 static final int ERRORED = MediaTracker.ERRORED; 902 static final int COMPLETE = MediaTracker.COMPLETE; 903 904 static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE); 905 static final int DONE = (ABORTED | ERRORED | COMPLETE); 906 907 synchronized int getStatus(boolean doLoad, boolean doVerify) { 908 if (doLoad && ((status & LOADSTARTED) == 0)) { 909 status = (status & ~ABORTED) | LOADING; 910 startLoad(); 911 } 912 return status; 913 } 914 915 void setStatus(int flag) { 916 synchronized (this) { 917 status = flag; 918 } 919 tracker.setDone(); 920 } 921 } 922 923 class ImageMediaEntry extends MediaEntry implements ImageObserver, 924 java.io.Serializable { 925 Image image; 926 int width; 927 int height; 928 929 /* 930 * JDK 1.1 serialVersionUID 931 */ 932 private static final long serialVersionUID = 4739377000350280650L; 933 934 ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) { 935 super(mt, c); 936 image = img; 937 width = w; 938 height = h; 939 } 940 941 boolean matches(Image img, int w, int h) { 942 return (image == img && width == w && height == h); 943 } 944 945 Object getMedia() { 946 return image; 947 } 948 949 synchronized int getStatus(boolean doLoad, boolean doVerify) { 950 if (doVerify) { 951 int flags = tracker.target.checkImage(image, width, height, null); 952 int s = parseflags(flags); 953 if (s == 0) { 954 if ((status & (ERRORED | COMPLETE)) != 0) { 955 setStatus(ABORTED); 956 } 957 } else if (s != status) { 958 setStatus(s); 959 } 960 } 961 return super.getStatus(doLoad, doVerify); 962 } 963 964 void startLoad() { 965 if (tracker.target.prepareImage(image, width, height, this)) { 966 setStatus(COMPLETE); 967 } 968 } 969 970 int parseflags(int infoflags) { 971 if ((infoflags & ERROR) != 0) { 972 return ERRORED; 973 } else if ((infoflags & ABORT) != 0) { 974 return ABORTED; 975 } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) { 976 return COMPLETE; 977 } 978 return 0; 979 } 980 981 public boolean imageUpdate(Image img, int infoflags, 982 int x, int y, int w, int h) { 983 if (cancelled) { 984 return false; 985 } 986 int s = parseflags(infoflags); 987 if (s != 0 && s != status) { 988 setStatus(s); 989 } 990 return ((status & LOADING) != 0); 991 } 992 }