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      */
 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      */
 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      */
 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 
 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      */
 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      */
 317     public boolean checkAll(boolean load) {
 318         return checkAll(load, true);
 319     }
 320 
 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      */
 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      */
 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      */
 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      */
 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      */
 473     public int statusAll(boolean load) {
 474         return statusAll(load, true);
 475     }
 476 
 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      */
 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      */
 534     public boolean checkID(int id, boolean load) {
 535         return checkID(id, load, true);
 536     }
 537 
 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      */
 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      */
 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      */
 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      */
 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      */
 709     public int statusID(int id, boolean load) {
 710         return statusID(id, load, true);
 711     }
 712 
 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      */
 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 
 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      */
 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 
 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      */
 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 
 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 
 846     synchronized void setDone() {
 847         notifyAll();
 848     }
 849 
 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 
 866     MediaEntry(MediaTracker mt, int id) {
 867         tracker = mt;
 868         ID = id;
 869     }
 870 
 871     abstract Object getMedia();
 872 
 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 
 892     int getID() {
 893         return ID;
 894     }
 895 
 896     abstract void startLoad();
 897 
 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 
 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 
 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 
 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 
 946     boolean matches(Image img, int w, int h) {
 947         return (image == img && width == w && height == h);
 948     }
 949 
 950     Object getMedia() {
 951         return image;
 952     }
 953 
 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 
 969     void startLoad() {
 970         if (tracker.target.prepareImage(image, width, height, this)) {
 971             setStatus(COMPLETE);
 972         }
 973     }
 974 
 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 
 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 }