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     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} or {@code isErrorID} methods to
 281      * check for errors.
 282      * @return      {@code true} if all images have finished loading,
 283      *                       have been aborted, or have encountered
 284      *                       an error; {@code false} 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} flag is {@code true},
 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} and {@code isErrorID} methods to
 305      * check for errors.
 306      * @param       load   if {@code true}, start loading any
 307      *                       images that are not yet being loaded
 308      * @return      {@code true} if all images have finished loading,
 309      *                       have been aborted, or have encountered
 310      *                       an error; {@code false} 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} if any of the images tracked
 335      *                  by this media tracker had an error during
 336      *                  loading; {@code false} 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} 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} or {@code isErrorID} 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} 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} or {@code isErrorID} 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} if all images were successfully
 417      *                       loaded; {@code false} 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} class are {@code LOADING},
 455      * {@code ABORTED}, {@code ERRORED}, and
 456      * {@code COMPLETE}. An image that hasn't started
 457      * loading has zero as its status.
 458      * <p>
 459      * If the value of {@code load} is {@code true}, then
 460      * this method starts loading any images that are not yet being loaded.
 461      *
 462      * @param        load   if {@code true}, 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} or {@code isErrorID} methods to
 496      * check for errors.
 497      * @param       id   the identifier of the images to check
 498      * @return      {@code true} if all images have finished loading,
 499      *                       have been aborted, or have encountered
 500      *                       an error; {@code false} 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} flag is {@code true},
 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} or {@code isErrorID} methods to
 521      * check for errors.
 522      * @param       id       the identifier of the images to check
 523      * @param       load     if {@code true}, start loading any
 524      *                       images that are not yet being loaded
 525      * @return      {@code true} if all images have finished loading,
 526      *                       have been aborted, or have encountered
 527      *                       an error; {@code false} 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} if any of the images with the
 557      *                          specified identifier had an error during
 558      *                          loading; {@code false} 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} 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} and {@code isErrorID} 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}
 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 isErrorID}, and
 645      * {@code isErrorAny} 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      * @return {@code true} if the loading completed in time;
 650      *         otherwise {@code false}
 651      * @see           java.awt.MediaTracker#waitForAll
 652      * @see           java.awt.MediaTracker#waitForID(int)
 653      * @see           java.awt.MediaTracker#statusID
 654      * @see           java.awt.MediaTracker#isErrorAny()
 655      * @see           java.awt.MediaTracker#isErrorID(int)
 656      * @exception     InterruptedException  if any thread has
 657      *                          interrupted this thread.
 658      */
 659     public synchronized boolean waitForID(int id, long ms)
 660         throws InterruptedException
 661     {
 662         long end = System.currentTimeMillis() + ms;
 663         boolean first = true;
 664         while (true) {
 665             int status = statusID(id, first, first);
 666             if ((status & LOADING) == 0) {
 667                 return (status == COMPLETE);
 668             }
 669             first = false;
 670             long timeout;
 671             if (ms == 0) {
 672                 timeout = 0;
 673             } else {
 674                 timeout = end - System.currentTimeMillis();
 675                 if (timeout <= 0) {
 676                     return false;
 677                 }
 678             }
 679             wait(timeout);
 680         }
 681     }
 682 
 683     /**
 684      * Calculates and returns the bitwise inclusive <b>OR</b> of the
 685      * status of all media with the specified identifier that are
 686      * tracked by this media tracker.
 687      * <p>
 688      * Possible flags defined by the
 689      * {@code MediaTracker} class are {@code LOADING},
 690      * {@code ABORTED}, {@code ERRORED}, and
 691      * {@code COMPLETE}. An image that hasn't started
 692      * loading has zero as its status.
 693      * <p>
 694      * If the value of {@code load} is {@code true}, then
 695      * this method starts loading any images that are not yet being loaded.
 696      * @param        id   the identifier of the images to check
 697      * @param        load   if {@code true}, start loading
 698      *                            any images that are not yet being loaded
 699      * @return       the bitwise inclusive <b>OR</b> of the status of
 700      *                            all of the media with the specified
 701      *                            identifier that are being tracked
 702      * @see          java.awt.MediaTracker#statusAll(boolean)
 703      * @see          java.awt.MediaTracker#LOADING
 704      * @see          java.awt.MediaTracker#ABORTED
 705      * @see          java.awt.MediaTracker#ERRORED
 706      * @see          java.awt.MediaTracker#COMPLETE
 707      */
 708     public int statusID(int id, boolean load) {
 709         return statusID(id, load, true);
 710     }
 711 
 712     private synchronized int statusID(int id, boolean load, boolean verify) {
 713         MediaEntry cur = head;
 714         int status = 0;
 715         while (cur != null) {
 716             if (cur.getID() == id) {
 717                 status = status | cur.getStatus(load, verify);
 718             }
 719             cur = cur.next;
 720         }
 721         return status;
 722     }
 723 
 724     /**
 725      * Removes the specified image from this media tracker.
 726      * All instances of the specified image are removed,
 727      * regardless of scale or ID.
 728      * @param   image     the image to be removed
 729      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 730      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 731      * @since   1.1
 732      */
 733     public synchronized void removeImage(Image image) {
 734         removeImageImpl(image);
 735         Image rvImage = getResolutionVariant(image);
 736         if (rvImage != null) {
 737             removeImageImpl(rvImage);
 738         }
 739         notifyAll();    // Notify in case remaining images are "done".
 740     }
 741 
 742     private void removeImageImpl(Image image) {
 743         MediaEntry cur = head;
 744         MediaEntry prev = null;
 745         while (cur != null) {
 746             MediaEntry next = cur.next;
 747             if (cur.getMedia() == image) {
 748                 if (prev == null) {
 749                     head = next;
 750                 } else {
 751                     prev.next = next;
 752                 }
 753                 cur.cancel();
 754             } else {
 755                 prev = cur;
 756             }
 757             cur = next;
 758         }
 759     }
 760 
 761     /**
 762      * Removes the specified image from the specified tracking
 763      * ID of this media tracker.
 764      * All instances of {@code Image} being tracked
 765      * under the specified ID are removed regardless of scale.
 766      * @param      image the image to be removed
 767      * @param      id the tracking ID from which to remove the image
 768      * @see        java.awt.MediaTracker#removeImage(java.awt.Image)
 769      * @see        java.awt.MediaTracker#removeImage(java.awt.Image, int, int, int)
 770      * @since      1.1
 771      */
 772     public synchronized void removeImage(Image image, int id) {
 773         removeImageImpl(image, id);
 774         Image rvImage = getResolutionVariant(image);
 775         if (rvImage != null) {
 776             removeImageImpl(rvImage, id);
 777         }
 778         notifyAll();    // Notify in case remaining images are "done".
 779     }
 780 
 781     private void removeImageImpl(Image image, int id) {
 782         MediaEntry cur = head;
 783         MediaEntry prev = null;
 784         while (cur != null) {
 785             MediaEntry next = cur.next;
 786             if (cur.getID() == id && cur.getMedia() == image) {
 787                 if (prev == null) {
 788                     head = next;
 789                 } else {
 790                     prev.next = next;
 791                 }
 792                 cur.cancel();
 793             } else {
 794                 prev = cur;
 795             }
 796             cur = next;
 797         }
 798     }
 799 
 800     /**
 801      * Removes the specified image with the specified
 802      * width, height, and ID from this media tracker.
 803      * Only the specified instance (with any duplicates) is removed.
 804      * @param   image the image to be removed
 805      * @param   id the tracking ID from which to remove the image
 806      * @param   width the width to remove (-1 for unscaled)
 807      * @param   height the height to remove (-1 for unscaled)
 808      * @see     java.awt.MediaTracker#removeImage(java.awt.Image)
 809      * @see     java.awt.MediaTracker#removeImage(java.awt.Image, int)
 810      * @since   1.1
 811      */
 812     public synchronized void removeImage(Image image, int id,
 813                                          int width, int height) {
 814         removeImageImpl(image, id, width, height);
 815         Image rvImage = getResolutionVariant(image);
 816         if (rvImage != null) {
 817             removeImageImpl(rvImage, id,
 818                     width == -1 ? -1 : 2 * width,
 819                     height == -1 ? -1 : 2 * height);
 820         }
 821         notifyAll();    // Notify in case remaining images are "done".
 822     }
 823 
 824     private void removeImageImpl(Image image, int id, int width, int height) {
 825         MediaEntry cur = head;
 826         MediaEntry prev = null;
 827         while (cur != null) {
 828             MediaEntry next = cur.next;
 829             if (cur.getID() == id && cur instanceof ImageMediaEntry
 830                 && ((ImageMediaEntry) cur).matches(image, width, height))
 831             {
 832                 if (prev == null) {
 833                     head = next;
 834                 } else {
 835                     prev.next = next;
 836                 }
 837                 cur.cancel();
 838             } else {
 839                 prev = cur;
 840             }
 841             cur = next;
 842         }
 843     }
 844 
 845     synchronized void setDone() {
 846         notifyAll();
 847     }
 848 
 849     private static Image getResolutionVariant(Image image) {
 850         if (image instanceof MultiResolutionToolkitImage) {
 851             return ((MultiResolutionToolkitImage) image).getResolutionVariant();
 852         }
 853         return null;
 854     }
 855 }
 856 
 857 abstract class MediaEntry {
 858     MediaTracker tracker;
 859     int ID;
 860     MediaEntry next;
 861 
 862     int status;
 863     boolean cancelled;
 864 
 865     MediaEntry(MediaTracker mt, int id) {
 866         tracker = mt;
 867         ID = id;
 868     }
 869 
 870     abstract Object getMedia();
 871 
 872     static MediaEntry insert(MediaEntry head, MediaEntry me) {
 873         MediaEntry cur = head;
 874         MediaEntry prev = null;
 875         while (cur != null) {
 876             if (cur.ID > me.ID) {
 877                 break;
 878             }
 879             prev = cur;
 880             cur = cur.next;
 881         }
 882         me.next = cur;
 883         if (prev == null) {
 884             head = me;
 885         } else {
 886             prev.next = me;
 887         }
 888         return head;
 889     }
 890 
 891     int getID() {
 892         return ID;
 893     }
 894 
 895     abstract void startLoad();
 896 
 897     void cancel() {
 898         cancelled = true;
 899     }
 900 
 901     static final int LOADING = MediaTracker.LOADING;
 902     static final int ABORTED = MediaTracker.ABORTED;
 903     static final int ERRORED = MediaTracker.ERRORED;
 904     static final int COMPLETE = MediaTracker.COMPLETE;
 905 
 906     static final int LOADSTARTED = (LOADING | ERRORED | COMPLETE);
 907     static final int DONE = (ABORTED | ERRORED | COMPLETE);
 908 
 909     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 910         if (doLoad && ((status & LOADSTARTED) == 0)) {
 911             status = (status & ~ABORTED) | LOADING;
 912             startLoad();
 913         }
 914         return status;
 915     }
 916 
 917     void setStatus(int flag) {
 918         synchronized (this) {
 919             status = flag;
 920         }
 921         tracker.setDone();
 922     }
 923 }
 924 
 925 class ImageMediaEntry extends MediaEntry implements ImageObserver,
 926 java.io.Serializable {
 927     Image image;
 928     int width;
 929     int height;
 930 
 931     /*
 932      * JDK 1.1 serialVersionUID
 933      */
 934     private static final long serialVersionUID = 4739377000350280650L;
 935 
 936     ImageMediaEntry(MediaTracker mt, Image img, int c, int w, int h) {
 937         super(mt, c);
 938         image = img;
 939         width = w;
 940         height = h;
 941     }
 942 
 943     boolean matches(Image img, int w, int h) {
 944         return (image == img && width == w && height == h);
 945     }
 946 
 947     Object getMedia() {
 948         return image;
 949     }
 950 
 951     synchronized int getStatus(boolean doLoad, boolean doVerify) {
 952         if (doVerify) {
 953             int flags = tracker.target.checkImage(image, width, height, null);
 954             int s = parseflags(flags);
 955             if (s == 0) {
 956                 if ((status & (ERRORED | COMPLETE)) != 0) {
 957                     setStatus(ABORTED);
 958                 }
 959             } else if (s != status) {
 960                 setStatus(s);
 961             }
 962         }
 963         return super.getStatus(doLoad, doVerify);
 964     }
 965 
 966     void startLoad() {
 967         if (tracker.target.prepareImage(image, width, height, this)) {
 968             setStatus(COMPLETE);
 969         }
 970     }
 971 
 972     int parseflags(int infoflags) {
 973         if ((infoflags & ERROR) != 0) {
 974             return ERRORED;
 975         } else if ((infoflags & ABORT) != 0) {
 976             return ABORTED;
 977         } else if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) {
 978             return COMPLETE;
 979         }
 980         return 0;
 981     }
 982 
 983     public boolean imageUpdate(Image img, int infoflags,
 984                                int x, int y, int w, int h) {
 985         if (cancelled) {
 986             return false;
 987         }
 988         int s = parseflags(infoflags);
 989         if (s != 0 && s != status) {
 990             setStatus(s);
 991         }
 992         return ((status & LOADING) != 0);
 993     }
 994 }