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