1 /*
   2  * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt;
  27 
  28 import java.awt.Component;
  29 import java.awt.Image;
  30 import java.awt.image.ImageObserver;
  31 import sun.awt.image.MultiResolutionToolkitImage;
  32 
  33 /**
  34  * The <code>MediaTracker</code> class is a utility class to track
  35  * the status of a number of media objects. Media objects could
  36  * include audio clips as well as images, though currently only
  37  * images are supported.
  38  * <p>
  39  * To use a media tracker, create an instance of
  40  * <code>MediaTracker</code> and call its <code>addImage</code>
  41  * method for each image to be tracked. In addition, each image can
  42  * be assigned a unique identifier. This identifier controls the
  43  * priority order in which the images are fetched. It can also be used
  44  * to identify unique subsets of the images that can be waited on
  45  * independently. Images with a lower ID are loaded in preference to
  46  * those with a higher ID number.
  47  *
  48  * <p>
  49  *
  50  * Tracking an animated image
  51  * might not always be useful
  52  * due to the multi-part nature of animated image
  53  * loading and painting,
  54  * but it is supported.
  55  * <code>MediaTracker</code> treats an animated image
  56  * as completely loaded
  57  * when the first frame is completely loaded.
  58  * At that point, the <code>MediaTracker</code>
  59  * signals any waiters
  60  * that the image is completely loaded.
  61  * If no <code>ImageObserver</code>s are observing the image
  62  * when the first frame has finished loading,
  63  * the image might flush itself
  64  * to conserve resources
  65  * (see {@link Image#flush()}).
  66  *
  67  * <p>
  68  * Here is an example of using <code>MediaTracker</code>:
  69  *
  70  * <hr><blockquote><pre>{@code
  71  * import java.applet.Applet;
  72  * import java.awt.Color;
  73  * import java.awt.Image;
  74  * import java.awt.Graphics;
  75  * import java.awt.MediaTracker;
  76  *
  77  * public class ImageBlaster extends Applet implements Runnable {
  78  *      MediaTracker tracker;
  79  *      Image bg;
  80  *      Image anim[] = new Image[5];
  81  *      int index;
  82  *      Thread animator;
  83  *
  84  *      // Get the images for the background (id == 0)
  85  *      // and the animation frames (id == 1)
  86  *      // and add them to the MediaTracker
  87  *      public void init() {
  88  *          tracker = new MediaTracker(this);
  89  *          bg = getImage(getDocumentBase(),
  90  *                  "images/background.gif");
  91  *          tracker.addImage(bg, 0);
  92  *          for (int i = 0; i < 5; i++) {
  93  *              anim[i] = getImage(getDocumentBase(),
  94  *                      "images/anim"+i+".gif");
  95  *              tracker.addImage(anim[i], 1);
  96  *          }
  97  *      }
  98  *
  99  *      // Start the animation thread.
 100  *      public void start() {
 101  *          animator = new Thread(this);
 102  *          animator.start();
 103  *      }
 104  *
 105  *      // Stop the animation thread.
 106  *      public void stop() {
 107  *          animator = null;
 108  *      }
 109  *
 110  *      // Run the animation thread.
 111  *      // First wait for the background image to fully load
 112  *      // and paint.  Then wait for all of the animation
 113  *      // frames to finish loading. Finally, loop and
 114  *      // increment the animation frame index.
 115  *      public void run() {
 116  *          try {
 117  *              tracker.waitForID(0);
 118  *              tracker.waitForID(1);
 119  *          } catch (InterruptedException e) {
 120  *              return;
 121  *          }
 122  *          Thread me = Thread.currentThread();
 123  *          while (animator == me) {
 124  *              try {
 125  *                  Thread.sleep(100);
 126  *              } catch (InterruptedException e) {
 127  *                  break;
 128  *              }
 129  *              synchronized (this) {
 130  *                  index++;
 131  *                  if (index >= anim.length) {
 132  *                      index = 0;
 133  *                  }
 134  *              }
 135  *              repaint();
 136  *          }
 137  *      }
 138  *
 139  *      // The background image fills the frame so we
 140  *      // don't need to clear the applet on repaints.
 141  *      // Just call the paint method.
 142  *      public void update(Graphics g) {
 143  *          paint(g);
 144  *      }
 145  *
 146  *      // Paint a large red rectangle if there are any errors
 147  *      // loading the images.  Otherwise always paint the
 148  *      // background so that it appears incrementally as it
 149  *      // is loading.  Finally, only paint the current animation
 150  *      // frame if all of the frames (id == 1) are done loading,
 151  *      // so that we don't get partial animations.
 152  *      public void paint(Graphics g) {
 153  *          if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) {
 154  *              g.setColor(Color.red);
 155  *              g.fillRect(0, 0, size().width, size().height);
 156  *              return;
 157  *          }
 158  *          g.drawImage(bg, 0, 0, this);
 159  *          if (tracker.statusID(1, false) == MediaTracker.COMPLETE) {
 160  *              g.drawImage(anim[index], 10, 10, this);
 161  *          }
 162  *      }
 163  * }
 164  * } </pre></blockquote><hr>
 165  *
 166  * @author      Jim Graham
 167  * @since       JDK1.0
 168  */
 169 public class MediaTracker implements java.io.Serializable {
 170 
 171     /**
 172      * A given <code>Component</code> that will be
 173      * tracked by a media tracker where the image will
 174      * eventually be drawn.
 175      *
 176      * @serial
 177      * @see #MediaTracker(Component)
 178      */
 179     Component target;
 180     /**
 181      * The head of the list of <code>Images</code> that is being
 182      * tracked by the <code>MediaTracker</code>.
 183      *
 184      * @serial
 185      * @see #addImage(Image, int)
 186      * @see #removeImage(Image)
 187      */
 188     MediaEntry head;
 189 
 190     /*
 191      * JDK 1.1 serialVersionUID
 192      */
 193     private static final long serialVersionUID = -483174189758638095L;
 194 
 195     /**
 196      * Creates a media tracker to track images for a given component.
 197      * @param     comp the component on which the images
 198      *                     will eventually be drawn
 199      */
 200     public MediaTracker(Component comp) {
 201         target = comp;
 202     }
 203 
 204     /**
 205      * Adds an image to the list of images being tracked by this media
 206      * tracker. The image will eventually be rendered at its default
 207      * (unscaled) size.
 208      * @param     image   the image to be tracked
 209      * @param     id      an identifier used to track this image
 210      */
 211     public void addImage(Image image, int id) {
 212         addImage(image, id, -1, -1);
 213     }
 214 
 215     /**
 216      * Adds a scaled image to the list of images being tracked
 217      * by this media tracker. The image will eventually be
 218      * rendered at the indicated width and height.
 219      *
 220      * @param     image   the image to be tracked
 221      * @param     id   an identifier that can be used to track this image
 222      * @param     w    the width at which the image is rendered
 223      * @param     h    the height at which the image is rendered
 224      */
 225     public synchronized void addImage(Image image, int id, int w, int h) {
 226         addImageImpl(image, id, w, h);
 227         Image rvImage = getResolutionVariant(image);
 228         if (rvImage != null) {
 229             addImageImpl(rvImage, id, 2 * w, 2 * h);
 230         }
 231     }
 232 
 233     private void addImageImpl(Image image, int id, int w, int h) {
 234         head = MediaEntry.insert(head,
 235                                  new ImageMediaEntry(this, image, id, w, h));
 236     }
 237     /**
 238      * Flag indicating that media is currently being loaded.
 239      * @see         java.awt.MediaTracker#statusAll
 240      * @see         java.awt.MediaTracker#statusID
 241      */
 242     public static final int LOADING = 1;
 243 
 244     /**
 245      * Flag indicating that the downloading of media was aborted.
 246      * @see         java.awt.MediaTracker#statusAll
 247      * @see         java.awt.MediaTracker#statusID
 248      */
 249     public static final int ABORTED = 2;
 250 
 251     /**
 252      * Flag indicating that the downloading of media encountered
 253      * an error.
 254      * @see         java.awt.MediaTracker#statusAll
 255      * @see         java.awt.MediaTracker#statusID
 256      */
 257     public static final int ERRORED = 4;
 258 
 259     /**
 260      * Flag indicating that the downloading of media was completed
 261      * successfully.
 262      * @see         java.awt.MediaTracker#statusAll
 263      * @see         java.awt.MediaTracker#statusID
 264      */
 265     public static final int COMPLETE = 8;
 266 
 267     static final int DONE = (ABORTED | ERRORED | COMPLETE);
 268 
 269     /**
 270      * Checks to see if all images being tracked by this media tracker
 271      * have finished loading.
 272      * <p>
 273      * This method does not start loading the images if they are not
 274      * already loading.
 275      * <p>
 276      * If there is an error while loading or scaling an image, then that
 277      * image is considered to have finished loading. Use the
 278      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 279      * check for errors.
 280      * @return      <code>true</code> if all images have finished loading,
 281      *                       have been aborted, or have encountered
 282      *                       an error; <code>false</code> otherwise
 283      * @see         java.awt.MediaTracker#checkAll(boolean)
 284      * @see         java.awt.MediaTracker#checkID
 285      * @see         java.awt.MediaTracker#isErrorAny
 286      * @see         java.awt.MediaTracker#isErrorID
 287      */
 288     public boolean checkAll() {
 289         return checkAll(false, true);
 290     }
 291 
 292     /**
 293      * Checks to see if all images being tracked by this media tracker
 294      * have finished loading.
 295      * <p>
 296      * If the value of the <code>load</code> flag is <code>true</code>,
 297      * then this method starts loading any images that are not yet
 298      * being loaded.
 299      * <p>
 300      * If there is an error while loading or scaling an image, that
 301      * image is considered to have finished loading. Use the
 302      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
 303      * check for errors.
 304      * @param       load   if <code>true</code>, start loading any
 305      *                       images that are not yet being loaded
 306      * @return      <code>true</code> if all images have finished loading,
 307      *                       have been aborted, or have encountered
 308      *                       an error; <code>false</code> otherwise
 309      * @see         java.awt.MediaTracker#checkID
 310      * @see         java.awt.MediaTracker#checkAll()
 311      * @see         java.awt.MediaTracker#isErrorAny()
 312      * @see         java.awt.MediaTracker#isErrorID(int)
 313      */
 314     public boolean checkAll(boolean load) {
 315         return checkAll(load, true);
 316     }
 317 
 318     private synchronized boolean checkAll(boolean load, boolean verify) {
 319         MediaEntry cur = head;
 320         boolean done = true;
 321         while (cur != null) {
 322             if ((cur.getStatus(load, verify) & DONE) == 0) {
 323                 done = false;
 324             }
 325             cur = cur.next;
 326         }
 327         return done;
 328     }
 329 
 330     /**
 331      * Checks the error status of all of the images.
 332      * @return   <code>true</code> if any of the images tracked
 333      *                  by this media tracker had an error during
 334      *                  loading; <code>false</code> otherwise
 335      * @see      java.awt.MediaTracker#isErrorID
 336      * @see      java.awt.MediaTracker#getErrorsAny
 337      */
 338     public synchronized boolean isErrorAny() {
 339         MediaEntry cur = head;
 340         while (cur != null) {
 341             if ((cur.getStatus(false, true) & ERRORED) != 0) {
 342                 return true;
 343             }
 344             cur = cur.next;
 345         }
 346         return false;
 347     }
 348 
 349     /**
 350      * Returns a list of all media that have encountered an error.
 351      * @return       an array of media objects tracked by this
 352      *                        media tracker that have encountered
 353      *                        an error, or <code>null</code> if
 354      *                        there are none with errors
 355      * @see          java.awt.MediaTracker#isErrorAny
 356      * @see          java.awt.MediaTracker#getErrorsID
 357      */
 358     public synchronized Object[] getErrorsAny() {
 359         MediaEntry cur = head;
 360         int numerrors = 0;
 361         while (cur != null) {
 362             if ((cur.getStatus(false, true) & ERRORED) != 0) {
 363                 numerrors++;
 364             }
 365             cur = cur.next;
 366         }
 367         if (numerrors == 0) {
 368             return null;
 369         }
 370         Object errors[] = new Object[numerrors];
 371         cur = head;
 372         numerrors = 0;
 373         while (cur != null) {
 374             if ((cur.getStatus(false, false) & ERRORED) != 0) {
 375                 errors[numerrors++] = cur.getMedia();
 376             }
 377             cur = cur.next;
 378         }
 379         return errors;
 380     }
 381 
 382     /**
 383      * Starts loading all images tracked by this media tracker. This
 384      * method waits until all the images being tracked have finished
 385      * loading.
 386      * <p>
 387      * If there is an error while loading or scaling an image, then that
 388      * image is considered to have finished loading. Use the
 389      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 390      * check for errors.
 391      * @see         java.awt.MediaTracker#waitForID(int)
 392      * @see         java.awt.MediaTracker#waitForAll(long)
 393      * @see         java.awt.MediaTracker#isErrorAny
 394      * @see         java.awt.MediaTracker#isErrorID
 395      * @exception   InterruptedException  if any thread has
 396      *                                     interrupted this thread
 397      */
 398     public void waitForAll() throws InterruptedException {
 399         waitForAll(0);
 400     }
 401 
 402     /**
 403      * Starts loading all images tracked by this media tracker. This
 404      * method waits until all the images being tracked have finished
 405      * loading, or until the length of time specified in milliseconds
 406      * by the <code>ms</code> argument has passed.
 407      * <p>
 408      * If there is an error while loading or scaling an image, then
 409      * that image is considered to have finished loading. Use the
 410      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 411      * check for errors.
 412      * @param       ms       the number of milliseconds to wait
 413      *                       for the loading to complete
 414      * @return      <code>true</code> if all images were successfully
 415      *                       loaded; <code>false</code> otherwise
 416      * @see         java.awt.MediaTracker#waitForID(int)
 417      * @see         java.awt.MediaTracker#waitForAll(long)
 418      * @see         java.awt.MediaTracker#isErrorAny
 419      * @see         java.awt.MediaTracker#isErrorID
 420      * @exception   InterruptedException  if any thread has
 421      *                                     interrupted this thread.
 422      */
 423     public synchronized boolean waitForAll(long ms)
 424         throws InterruptedException
 425     {
 426         long end = System.currentTimeMillis() + ms;
 427         boolean first = true;
 428         while (true) {
 429             int status = statusAll(first, first);
 430             if ((status & LOADING) == 0) {
 431                 return (status == COMPLETE);
 432             }
 433             first = false;
 434             long timeout;
 435             if (ms == 0) {
 436                 timeout = 0;
 437             } else {
 438                 timeout = end - System.currentTimeMillis();
 439                 if (timeout <= 0) {
 440                     return false;
 441                 }
 442             }
 443             wait(timeout);
 444         }
 445     }
 446 
 447     /**
 448      * Calculates and returns the bitwise inclusive <b>OR</b> of the
 449      * status of all media that are tracked by this media tracker.
 450      * <p>
 451      * Possible flags defined by the
 452      * <code>MediaTracker</code> class are <code>LOADING</code>,
 453      * <code>ABORTED</code>, <code>ERRORED</code>, and
 454      * <code>COMPLETE</code>. An image that hasn't started
 455      * loading has zero as its status.
 456      * <p>
 457      * If the value of <code>load</code> is <code>true</code>, then
 458      * this method starts loading any images that are not yet being loaded.
 459      *
 460      * @param        load   if <code>true</code>, start loading
 461      *                            any images that are not yet being loaded
 462      * @return       the bitwise inclusive <b>OR</b> of the status of
 463      *                            all of the media being tracked
 464      * @see          java.awt.MediaTracker#statusID(int, boolean)
 465      * @see          java.awt.MediaTracker#LOADING
 466      * @see          java.awt.MediaTracker#ABORTED
 467      * @see          java.awt.MediaTracker#ERRORED
 468      * @see          java.awt.MediaTracker#COMPLETE
 469      */
 470     public int statusAll(boolean load) {
 471         return statusAll(load, true);
 472     }
 473 
 474     private synchronized int statusAll(boolean load, boolean verify) {
 475         MediaEntry cur = head;
 476         int status = 0;
 477         while (cur != null) {
 478             status = status | cur.getStatus(load, verify);
 479             cur = cur.next;
 480         }
 481         return status;
 482     }
 483 
 484     /**
 485      * Checks to see if all images tracked by this media tracker that
 486      * are tagged with the specified identifier have finished loading.
 487      * <p>
 488      * This method does not start loading the images if they are not
 489      * already loading.
 490      * <p>
 491      * If there is an error while loading or scaling an image, then that
 492      * image is considered to have finished loading. Use the
 493      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 494      * check for errors.
 495      * @param       id   the identifier of the images to check
 496      * @return      <code>true</code> if all images have finished loading,
 497      *                       have been aborted, or have encountered
 498      *                       an error; <code>false</code> otherwise
 499      * @see         java.awt.MediaTracker#checkID(int, boolean)
 500      * @see         java.awt.MediaTracker#checkAll()
 501      * @see         java.awt.MediaTracker#isErrorAny()
 502      * @see         java.awt.MediaTracker#isErrorID(int)
 503      */
 504     public boolean checkID(int id) {
 505         return checkID(id, false, true);
 506     }
 507 
 508     /**
 509      * Checks to see if all images tracked by this media tracker that
 510      * are tagged with the specified identifier have finished loading.
 511      * <p>
 512      * If the value of the <code>load</code> flag is <code>true</code>,
 513      * then this method starts loading any images that are not yet
 514      * being loaded.
 515      * <p>
 516      * If there is an error while loading or scaling an image, then that
 517      * image is considered to have finished loading. Use the
 518      * <code>isErrorAny</code> or <code>isErrorID</code> methods to
 519      * check for errors.
 520      * @param       id       the identifier of the images to check
 521      * @param       load     if <code>true</code>, start loading any
 522      *                       images that are not yet being loaded
 523      * @return      <code>true</code> if all images have finished loading,
 524      *                       have been aborted, or have encountered
 525      *                       an error; <code>false</code> otherwise
 526      * @see         java.awt.MediaTracker#checkID(int, boolean)
 527      * @see         java.awt.MediaTracker#checkAll()
 528      * @see         java.awt.MediaTracker#isErrorAny()
 529      * @see         java.awt.MediaTracker#isErrorID(int)
 530      */
 531     public boolean checkID(int id, boolean load) {
 532         return checkID(id, load, true);
 533     }
 534 
 535     private synchronized boolean checkID(int id, boolean load, boolean verify)
 536     {
 537         MediaEntry cur = head;
 538         boolean done = true;
 539         while (cur != null) {
 540             if (cur.getID() == id
 541                 && (cur.getStatus(load, verify) & DONE) == 0)
 542             {
 543                 done = false;
 544             }
 545             cur = cur.next;
 546         }
 547         return done;
 548     }
 549 
 550     /**
 551      * Checks the error status of all of the images tracked by this
 552      * media tracker with the specified identifier.
 553      * @param        id   the identifier of the images to check
 554      * @return       <code>true</code> if any of the images with the
 555      *                          specified identifier had an error during
 556      *                          loading; <code>false</code> otherwise
 557      * @see          java.awt.MediaTracker#isErrorAny
 558      * @see          java.awt.MediaTracker#getErrorsID
 559      */
 560     public synchronized boolean isErrorID(int id) {
 561         MediaEntry cur = head;
 562         while (cur != null) {
 563             if (cur.getID() == id
 564                 && (cur.getStatus(false, true) & ERRORED) != 0)
 565             {
 566                 return true;
 567             }
 568             cur = cur.next;
 569         }
 570         return false;
 571     }
 572 
 573     /**
 574      * Returns a list of media with the specified ID that
 575      * have encountered an error.
 576      * @param       id   the identifier of the images to check
 577      * @return      an array of media objects tracked by this media
 578      *                       tracker with the specified identifier
 579      *                       that have encountered an error, or
 580      *                       <code>null</code> if there are none with errors
 581      * @see         java.awt.MediaTracker#isErrorID
 582      * @see         java.awt.MediaTracker#isErrorAny
 583      * @see         java.awt.MediaTracker#getErrorsAny
 584      */
 585     public synchronized Object[] getErrorsID(int id) {
 586         MediaEntry cur = head;
 587         int numerrors = 0;
 588         while (cur != null) {
 589             if (cur.getID() == id
 590                 && (cur.getStatus(false, true) & ERRORED) != 0)
 591             {
 592                 numerrors++;
 593             }
 594             cur = cur.next;
 595         }
 596         if (numerrors == 0) {
 597             return null;
 598         }
 599         Object errors[] = new Object[numerrors];
 600         cur = head;
 601         numerrors = 0;
 602         while (cur != null) {
 603             if (cur.getID() == id
 604                 && (cur.getStatus(false, false) & ERRORED) != 0)
 605             {
 606                 errors[numerrors++] = cur.getMedia();
 607             }
 608             cur = cur.next;
 609         }
 610         return errors;
 611     }
 612 
 613     /**
 614      * Starts loading all images tracked by this media tracker with the
 615      * specified identifier. This method waits until all the images with
 616      * the specified identifier have finished loading.
 617      * <p>
 618      * If there is an error while loading or scaling an image, then that
 619      * image is considered to have finished loading. Use the
 620      * <code>isErrorAny</code> and <code>isErrorID</code> methods to
 621      * check for errors.
 622      * @param         id   the identifier of the images to check
 623      * @see           java.awt.MediaTracker#waitForAll
 624      * @see           java.awt.MediaTracker#isErrorAny()
 625      * @see           java.awt.MediaTracker#isErrorID(int)
 626      * @exception     InterruptedException  if any thread has
 627      *                          interrupted this thread.
 628      */
 629     public void waitForID(int id) throws InterruptedException {
 630         waitForID(id, 0);
 631     }
 632 
 633     /**
 634      * Starts loading all images tracked by this media tracker with the
 635      * specified identifier. This method waits until all the images with
 636      * the specified identifier have finished loading, or until the
 637      * length of time specified in milliseconds by the <code>ms</code>
 638      * argument has passed.
 639      * <p>
 640      * If there is an error while loading or scaling an image, then that
 641      * image is considered to have finished loading. Use the
 642      * <code>statusID</code>, <code>isErrorID</code>, and
 643      * <code>isErrorAny</code> methods to check for errors.
 644      * @param  id the identifier of the images to check
 645      * @param  ms the length of time, in milliseconds, to wait
 646      *         for the loading to complete
 647      * @return {@code true} if the loading completed in time;
 648      *         otherwise {@code false}
 649      * @see           java.awt.MediaTracker#waitForAll
 650      * @see           java.awt.MediaTracker#waitForID(int)
 651      * @see           java.awt.MediaTracker#statusID
 652      * @see           java.awt.MediaTracker#isErrorAny()
 653      * @see           java.awt.MediaTracker#isErrorID(int)
 654      * @exception     InterruptedException  if any thread has
 655      *                          interrupted this thread.
 656      */
 657     public synchronized boolean waitForID(int id, long ms)
 658         throws InterruptedException
 659     {
 660         long end = System.currentTimeMillis() + ms;
 661         boolean first = true;
 662         while (true) {
 663             int status = statusID(id, first, first);
 664             if ((status & LOADING) == 0) {
 665                 return (status == COMPLETE);
 666             }
 667             first = false;
 668             long timeout;
 669             if (ms == 0) {
 670                 timeout = 0;
 671             } else {
 672                 timeout = end - System.currentTimeMillis();
 673                 if (timeout <= 0) {
 674                     return false;
 675                 }
 676             }
 677             wait(timeout);
 678         }
 679     }
 680 
 681     /**
 682      * Calculates and returns the bitwise inclusive <b>OR</b> of the
 683      * status of all media with the specified identifier that are
 684      * tracked by this media tracker.
 685      * <p>
 686      * Possible flags defined by the
 687      * <code>MediaTracker</code> class are <code>LOADING</code>,
 688      * <code>ABORTED</code>, <code>ERRORED</code>, and
 689      * <code>COMPLETE</code>. An image that hasn't started
 690      * loading has zero as its status.
 691      * <p>
 692      * If the value of <code>load</code> is <code>true</code>, then
 693      * this method starts loading any images that are not yet being loaded.
 694      * @param        id   the identifier of the images to check
 695      * @param        load   if <code>true</code>, start loading
 696      *                            any images that are not yet being loaded
 697      * @return       the bitwise inclusive <b>OR</b> of the status of
 698      *                            all of the media with the specified
 699      *                            identifier that are being tracked
 700      * @see          java.awt.MediaTracker#statusAll(boolean)
 701      * @see          java.awt.MediaTracker#LOADING
 702      * @see          java.awt.MediaTracker#ABORTED
 703      * @see          java.awt.MediaTracker#ERRORED
 704      * @see          java.awt.MediaTracker#COMPLETE
 705      */
 706     public int statusID(int id, boolean load) {
 707         return statusID(id, load, true);
 708     }
 709 
 710     private synchronized int statusID(int id, boolean load, boolean verify) {
 711         MediaEntry cur = head;
 712         int status = 0;
 713         while (cur != null) {
 714             if (cur.getID() == id) {
 715                 status = status | cur.getStatus(load, verify);
 716             }
 717             cur = cur.next;
 718         }
 719         return status;
 720     }
 721 
 722     /**
 723      * Removes the specified image from this media tracker.
 724      * All instances of the specified image are removed,
 725      * regardless of scale or ID.
 726      * @param   image     the image to be removed
 727      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 728      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 729      * @since   JDK1.1
 730      */
 731     public synchronized void removeImage(Image image) {
 732         removeImageImpl(image);
 733         Image rvImage = getResolutionVariant(image);
 734         if (rvImage != null) {
 735             removeImageImpl(rvImage);
 736         }
 737         notifyAll();    // Notify in case remaining images are "done".
 738     }
 739 
 740     private void removeImageImpl(Image image) {
 741         MediaEntry cur = head;
 742         MediaEntry prev = null;
 743         while (cur != null) {
 744             MediaEntry next = cur.next;
 745             if (cur.getMedia() == image) {
 746                 if (prev == null) {
 747                     head = next;
 748                 } else {
 749                     prev.next = next;
 750                 }
 751                 cur.cancel();
 752             } else {
 753                 prev = cur;
 754             }
 755             cur = next;
 756         }
 757     }
 758 
 759     /**
 760      * Removes the specified image from the specified tracking
 761      * ID of this media tracker.
 762      * All instances of <code>Image</code> being tracked
 763      * under the specified ID are removed regardless of scale.
 764      * @param      image the image to be removed
 765      * @param      id the tracking ID from which to remove the image
 766      * @see        java.awt.MediaTracker#removeImage(java.awt.Image)
 767      * @see        java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 768      * @since      JDK1.1
 769      */
 770     public synchronized void removeImage(Image image, int id) {
 771         removeImageImpl(image, id);
 772         Image rvImage = getResolutionVariant(image);
 773         if (rvImage != null) {
 774             removeImageImpl(rvImage, id);
 775         }
 776         notifyAll();    // Notify in case remaining images are "done".
 777     }
 778 
 779     private void removeImageImpl(Image image, int id) {
 780         MediaEntry cur = head;
 781         MediaEntry prev = null;
 782         while (cur != null) {
 783             MediaEntry next = cur.next;
 784             if (cur.getID() == id && cur.getMedia() == image) {
 785                 if (prev == null) {
 786                     head = next;
 787                 } else {
 788                     prev.next = next;
 789                 }
 790                 cur.cancel();
 791             } else {
 792                 prev = cur;
 793             }
 794             cur = next;
 795         }
 796     }
 797 
 798     /**
 799      * Removes the specified image with the specified
 800      * width, height, and ID from this media tracker.
 801      * Only the specified instance (with any duplicates) is removed.
 802      * @param   image the image to be removed
 803      * @param   id the tracking ID from which to remove the image
 804      * @param   width the width to remove (-1 for unscaled)
 805      * @param   height the height to remove (-1 for unscaled)
 806      * @see     java.awt.MediaTracker#removeImage(java.awt.Image)
 807      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 808      * @since   JDK1.1
 809      */
 810     public synchronized void removeImage(Image image, int id,
 811                                          int width, int height) {
 812         removeImageImpl(image, id, width, height);
 813         Image rvImage = getResolutionVariant(image);
 814         if (rvImage != null) {
 815             removeImageImpl(rvImage, id, 2 * width, 2 * height);
 816 
 817         }
 818         notifyAll();    // Notify in case remaining images are "done".
 819     }
 820 
 821     private void removeImageImpl(Image image, int id, int width, int height) {
 822         MediaEntry cur = head;
 823         MediaEntry prev = null;
 824         while (cur != null) {
 825             MediaEntry next = cur.next;
 826             if (cur.getID() == id && cur instanceof ImageMediaEntry
 827                 && ((ImageMediaEntry) cur).matches(image, width, height))
 828             {
 829                 if (prev == null) {
 830                     head = next;
 831                 } else {
 832                     prev.next = next;
 833                 }
 834                 cur.cancel();
 835             } else {
 836                 prev = cur;
 837             }
 838             cur = next;
 839         }
 840     }
 841 
 842     synchronized void setDone() {
 843         notifyAll();
 844     }
 845 
 846     private static Image getResolutionVariant(Image image) {
 847         if (image instanceof MultiResolutionToolkitImage) {
 848             return ((MultiResolutionToolkitImage) image).getResolutionVariant();
 849         }
 850         return null;
 851     }
 852 }
 853 
 854 abstract class MediaEntry {
 855     MediaTracker tracker;
 856     int ID;
 857     MediaEntry next;
 858 
 859     int status;
 860     boolean cancelled;
 861 
 862     MediaEntry(MediaTracker mt, int id) {
 863         tracker = mt;
 864         ID = id;
 865     }
 866 
 867     abstract Object getMedia();
 868 
 869     static MediaEntry insert(MediaEntry head, MediaEntry me) {
 870         MediaEntry cur = head;
 871         MediaEntry prev = null;
 872         while (cur != null) {
 873             if (cur.ID > me.ID) {
 874                 break;
 875             }
 876             prev = cur;
 877             cur = cur.next;
 878         }
 879         me.next = cur;
 880         if (prev == null) {
 881             head = me;
 882         } else {
 883             prev.next = me;
 884         }
 885         return head;
 886     }
 887 
 888     int getID() {
 889         return ID;
 890     }
 891 
 892     abstract void startLoad();
 893 
 894     void cancel() {
 895         cancelled = true;
 896     }
 897 
 898     static final int LOADING = MediaTracker.LOADING;
 899     static final int ABORTED = MediaTracker.ABORTED;
 900     static final int ERRORED = MediaTracker.ERRORED;
 901     static final int COMPLETE = MediaTracker.COMPLETE;
 902 
 903     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
 904     static final int DONE = (ABORTED | ERRORED | COMPLETE);
 905 
 906     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 907         if (doLoad && ((status & LOADSTARTED) == 0)) {
 908             status = (status & ~ABORTED) | LOADING;
 909             startLoad();
 910         }
 911         return status;
 912     }
 913 
 914     void setStatus(int flag) {
 915         synchronized (this) {
 916             status = flag;
 917         }
 918         tracker.setDone();
 919     }
 920 }
 921 
 922 class ImageMediaEntry extends MediaEntry implements ImageObserver,
 923 java.io.Serializable {
 924     Image image;
 925     int width;
 926     int height;
 927 
 928     /*
 929      * JDK 1.1 serialVersionUID
 930      */
 931     private static final long serialVersionUID = 4739377000350280650L;
 932 
 933     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
 934         super(mt, c);
 935         image = img;
 936         width = w;
 937         height = h;
 938     }
 939 
 940     boolean matches(Image img, int w, int h) {
 941         return (image == img && width == w && height == h);
 942     }
 943 
 944     Object getMedia() {
 945         return image;
 946     }
 947 
 948     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 949         if (doVerify) {
 950             int flags = tracker.target.checkImage(image, width, height, null);
 951             int s = parseflags(flags);
 952             if (s == 0) {
 953                 if ((status & (ERRORED | COMPLETE)) != 0) {
 954                     setStatus(ABORTED);
 955                 }
 956             } else if (s != status) {
 957                 setStatus(s);
 958             }
 959         }
 960         return super.getStatus(doLoad, doVerify);
 961     }
 962 
 963     void startLoad() {
 964         if (tracker.target.prepareImage(image, width, height, this)) {
 965             setStatus(COMPLETE);
 966         }
 967     }
 968 
 969     int parseflags(int infoflags) {
 970         if ((infoflags & ERROR) != 0) {
 971             return ERRORED;
 972         } else if ((infoflags & ABORT) != 0) {
 973             return ABORTED;
 974         } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
 975             return COMPLETE;
 976         }
 977         return 0;
 978     }
 979 
 980     public boolean imageUpdate(Image img, int infoflags,
 981                                int x, int y, int w, int h) {
 982         if (cancelled) {
 983             return false;
 984         }
 985         int s = parseflags(infoflags);
 986         if (s != 0 && s != status) {
 987             setStatus(s);
 988         }
 989         return ((status & LOADING) != 0);
 990     }
 991 }