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