1 /*
   2  * Copyright (c) 1997, 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 package javax.swing;
  26 
  27 import java.awt.*;
  28 import java.awt.image.*;
  29 import java.beans.ConstructorProperties;
  30 import java.beans.Transient;
  31 import java.net.URL;
  32 
  33 import java.io.Serializable;
  34 import java.io.ObjectOutputStream;
  35 import java.io.ObjectInputStream;
  36 import java.io.IOException;
  37 
  38 import java.util.Locale;
  39 import javax.accessibility.*;
  40 
  41 import sun.awt.AppContext;
  42 import java.lang.reflect.Field;
  43 import java.security.*;
  44 
  45 /**
  46  * An implementation of the Icon interface that paints Icons
  47  * from Images. Images that are created from a URL, filename or byte array
  48  * are preloaded using MediaTracker to monitor the loaded state
  49  * of the image.
  50  *
  51  * <p>
  52  * For further information and examples of using image icons, see
  53  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html">How to Use Icons</a>
  54  * in <em>The Java Tutorial.</em>
  55  *
  56  * <p>
  57  * <strong>Warning:</strong>
  58  * Serialized objects of this class will not be compatible with
  59  * future Swing releases. The current serialization support is
  60  * appropriate for short term storage or RMI between applications running
  61  * the same version of Swing.  As of 1.4, support for long term storage
  62  * of all JavaBeans&trade;
  63  * has been added to the <code>java.beans</code> package.
  64  * Please see {@link java.beans.XMLEncoder}.
  65  *
  66  * @author Jeff Dinkins
  67  * @author Lynn Monsanto
  68  */
  69 @SuppressWarnings("serial") // Same-version serialization only
  70 public class ImageIcon implements Icon, Serializable, Accessible {
  71     /* Keep references to the filename and location so that
  72      * alternate persistence schemes have the option to archive
  73      * images symbolically rather than including the image data
  74      * in the archive.
  75      */
  76     transient private String filename;
  77     transient private URL location;
  78 
  79     transient Image image;
  80     transient int loadStatus = 0;
  81     ImageObserver imageObserver;
  82     String description = null;
  83 
  84     /**
  85      * Do not use this shared component, which is used to track image loading.
  86      * It is left for backward compatibility only.
  87      * @deprecated since 1.8
  88      */
  89     @Deprecated
  90     protected final static Component component;
  91 
  92     /**
  93      * Do not use this shared media tracker, which is used to load images.
  94      * It is left for backward compatibility only.
  95      * @deprecated since 1.8
  96      */
  97     @Deprecated
  98     protected final static MediaTracker tracker;
  99 
 100     static {
 101         component = AccessController.doPrivileged(new PrivilegedAction<Component>() {
 102             public Component run() {
 103                 try {
 104                     final Component component = createNoPermsComponent();
 105 
 106                     // 6482575 - clear the appContext field so as not to leak it
 107                     Field appContextField =
 108 
 109                             Component.class.getDeclaredField("appContext");
 110                     appContextField.setAccessible(true);
 111                     appContextField.set(component, null);
 112 
 113                     return component;
 114                 } catch (Throwable e) {
 115                     // We don't care about component.
 116                     // So don't prevent class initialisation.
 117                     e.printStackTrace();
 118                     return null;
 119                 }
 120             }
 121         });
 122         tracker = new MediaTracker(component);
 123     }
 124 
 125     private static Component createNoPermsComponent() {
 126         // 7020198 - set acc field to no permissions and no subject
 127         // Note, will have appContext set.
 128         return AccessController.doPrivileged(
 129                 new PrivilegedAction<Component>() {
 130                     public Component run() {
 131                         return new Component() {
 132                         };
 133                     }
 134                 },
 135                 new AccessControlContext(new ProtectionDomain[]{
 136                         new ProtectionDomain(null, null)
 137                 })
 138         );
 139     }
 140 
 141     /**
 142      * Id used in loading images from MediaTracker.
 143      */
 144     private static int mediaTrackerID;
 145 
 146     private final static Object TRACKER_KEY = new StringBuilder("TRACKER_KEY");
 147 
 148     int width = -1;
 149     int height = -1;
 150 
 151     /**
 152      * Creates an ImageIcon from the specified file. The image will
 153      * be preloaded by using MediaTracker to monitor the loading state
 154      * of the image.
 155      * @param filename the name of the file containing the image
 156      * @param description a brief textual description of the image
 157      * @see #ImageIcon(String)
 158      */
 159     public ImageIcon(String filename, String description) {
 160         image = Toolkit.getDefaultToolkit().getImage(filename);
 161         if (image == null) {
 162             return;
 163         }
 164         this.filename = filename;
 165         this.description = description;
 166         loadImage(image);
 167     }
 168 
 169     /**
 170      * Creates an ImageIcon from the specified file. The image will
 171      * be preloaded by using MediaTracker to monitor the loading state
 172      * of the image. The specified String can be a file name or a
 173      * file path. When specifying a path, use the Internet-standard
 174      * forward-slash ("/") as a separator.
 175      * (The string is converted to an URL, so the forward-slash works
 176      * on all systems.)
 177      * For example, specify:
 178      * <pre>
 179      *    new ImageIcon("images/myImage.gif") </pre>
 180      * The description is initialized to the <code>filename</code> string.
 181      *
 182      * @param filename a String specifying a filename or path
 183      * @see #getDescription
 184      */
 185     @ConstructorProperties({"description"})
 186     public ImageIcon (String filename) {
 187         this(filename, filename);
 188     }
 189 
 190     /**
 191      * Creates an ImageIcon from the specified URL. The image will
 192      * be preloaded by using MediaTracker to monitor the loaded state
 193      * of the image.
 194      * @param location the URL for the image
 195      * @param description a brief textual description of the image
 196      * @see #ImageIcon(String)
 197      */
 198     public ImageIcon(URL location, String description) {
 199         image = Toolkit.getDefaultToolkit().getImage(location);
 200         if (image == null) {
 201             return;
 202         }
 203         this.location = location;
 204         this.description = description;
 205         loadImage(image);
 206     }
 207 
 208     /**
 209      * Creates an ImageIcon from the specified URL. The image will
 210      * be preloaded by using MediaTracker to monitor the loaded state
 211      * of the image.
 212      * The icon's description is initialized to be
 213      * a string representation of the URL.
 214      * @param location the URL for the image
 215      * @see #getDescription
 216      */
 217     public ImageIcon (URL location) {
 218         this(location, location.toExternalForm());
 219     }
 220 
 221     /**
 222      * Creates an ImageIcon from the image.
 223      * @param image the image
 224      * @param description a brief textual description of the image
 225      */
 226     public ImageIcon(Image image, String description) {
 227         this(image);
 228         this.description = description;
 229     }
 230 
 231     /**
 232      * Creates an ImageIcon from an image object.
 233      * If the image has a "comment" property that is a string,
 234      * then the string is used as the description of this icon.
 235      * @param image the image
 236      * @see #getDescription
 237      * @see java.awt.Image#getProperty
 238      */
 239     public ImageIcon (Image image) {
 240         this.image = image;
 241         Object o = image.getProperty("comment", imageObserver);
 242         if (o instanceof String) {
 243             description = (String) o;
 244         }
 245         loadImage(image);
 246     }
 247 
 248     /**
 249      * Creates an ImageIcon from an array of bytes which were
 250      * read from an image file containing a supported image format,
 251      * such as GIF, JPEG, or (as of 1.3) PNG.
 252      * Normally this array is created
 253      * by reading an image using Class.getResourceAsStream(), but
 254      * the byte array may also be statically stored in a class.
 255      *
 256      * @param  imageData an array of pixels in an image format supported
 257      *         by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG
 258      * @param  description a brief textual description of the image
 259      * @see    java.awt.Toolkit#createImage
 260      */
 261     public ImageIcon (byte[] imageData, String description) {
 262         this.image = Toolkit.getDefaultToolkit().createImage(imageData);
 263         if (image == null) {
 264             return;
 265         }
 266         this.description = description;
 267         loadImage(image);
 268     }
 269 
 270     /**
 271      * Creates an ImageIcon from an array of bytes which were
 272      * read from an image file containing a supported image format,
 273      * such as GIF, JPEG, or (as of 1.3) PNG.
 274      * Normally this array is created
 275      * by reading an image using Class.getResourceAsStream(), but
 276      * the byte array may also be statically stored in a class.
 277      * If the resulting image has a "comment" property that is a string,
 278      * then the string is used as the description of this icon.
 279      *
 280      * @param  imageData an array of pixels in an image format supported by
 281      *             the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG
 282      * @see    java.awt.Toolkit#createImage
 283      * @see #getDescription
 284      * @see java.awt.Image#getProperty
 285      */
 286     public ImageIcon (byte[] imageData) {
 287         this.image = Toolkit.getDefaultToolkit().createImage(imageData);
 288         if (image == null) {
 289             return;
 290         }
 291         Object o = image.getProperty("comment", imageObserver);
 292         if (o instanceof String) {
 293             description = (String) o;
 294         }
 295         loadImage(image);
 296     }
 297 
 298     /**
 299      * Creates an uninitialized image icon.
 300      */
 301     public ImageIcon() {
 302     }
 303 
 304     /**
 305      * Loads the image, returning only when the image is loaded.
 306      * @param image the image
 307      */
 308     protected void loadImage(Image image) {
 309         MediaTracker mTracker = getTracker();
 310         synchronized(mTracker) {
 311             int id = getNextID();
 312 
 313             mTracker.addImage(image, id);
 314             try {
 315                 mTracker.waitForID(id, 0);
 316             } catch (InterruptedException e) {
 317                 System.out.println("INTERRUPTED while loading Image");
 318             }
 319             loadStatus = mTracker.statusID(id, false);
 320             mTracker.removeImage(image, id);
 321 
 322             width = image.getWidth(imageObserver);
 323             height = image.getHeight(imageObserver);
 324         }
 325     }
 326 
 327     /**
 328      * Returns an ID to use with the MediaTracker in loading an image.
 329      */
 330     private int getNextID() {
 331         synchronized(getTracker()) {
 332             return ++mediaTrackerID;
 333         }
 334     }
 335 
 336     /**
 337      * Returns the MediaTracker for the current AppContext, creating a new
 338      * MediaTracker if necessary.
 339      */
 340     private MediaTracker getTracker() {
 341         Object trackerObj;
 342         AppContext ac = AppContext.getAppContext();
 343         // Opt: Only synchronize if trackerObj comes back null?
 344         // If null, synchronize, re-check for null, and put new tracker
 345         synchronized(ac) {
 346             trackerObj = ac.get(TRACKER_KEY);
 347             if (trackerObj == null) {
 348                 Component comp = new Component() {};
 349                 trackerObj = new MediaTracker(comp);
 350                 ac.put(TRACKER_KEY, trackerObj);
 351             }
 352         }
 353         return (MediaTracker) trackerObj;
 354     }
 355 
 356     /**
 357      * Returns the status of the image loading operation.
 358      * @return the loading status as defined by java.awt.MediaTracker
 359      * @see java.awt.MediaTracker#ABORTED
 360      * @see java.awt.MediaTracker#ERRORED
 361      * @see java.awt.MediaTracker#COMPLETE
 362      */
 363     public int getImageLoadStatus() {
 364         return loadStatus;
 365     }
 366 
 367     /**
 368      * Returns this icon's <code>Image</code>.
 369      * @return the <code>Image</code> object for this <code>ImageIcon</code>
 370      */
 371     @Transient
 372     public Image getImage() {
 373         return image;
 374     }
 375 
 376     /**
 377      * Sets the image displayed by this icon.
 378      * @param image the image
 379      */
 380     public void setImage(Image image) {
 381         this.image = image;
 382         loadImage(image);
 383     }
 384 
 385     /**
 386      * Gets the description of the image.  This is meant to be a brief
 387      * textual description of the object.  For example, it might be
 388      * presented to a blind user to give an indication of the purpose
 389      * of the image.
 390      * The description may be null.
 391      *
 392      * @return a brief textual description of the image
 393      */
 394     public String getDescription() {
 395         return description;
 396     }
 397 
 398     /**
 399      * Sets the description of the image.  This is meant to be a brief
 400      * textual description of the object.  For example, it might be
 401      * presented to a blind user to give an indication of the purpose
 402      * of the image.
 403      * @param description a brief textual description of the image
 404      */
 405     public void setDescription(String description) {
 406         this.description = description;
 407     }
 408 
 409     /**
 410      * Paints the icon.
 411      * The top-left corner of the icon is drawn at
 412      * the point (<code>x</code>, <code>y</code>)
 413      * in the coordinate space of the graphics context <code>g</code>.
 414      * If this icon has no image observer,
 415      * this method uses the <code>c</code> component
 416      * as the observer.
 417      *
 418      * @param c the component to be used as the observer
 419      *          if this icon has no image observer
 420      * @param g the graphics context
 421      * @param x the X coordinate of the icon's top-left corner
 422      * @param y the Y coordinate of the icon's top-left corner
 423      */
 424     public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
 425         if(imageObserver == null) {
 426            g.drawImage(image, x, y, c);
 427         } else {
 428            g.drawImage(image, x, y, imageObserver);
 429         }
 430     }
 431 
 432     /**
 433      * Gets the width of the icon.
 434      *
 435      * @return the width in pixels of this icon
 436      */
 437     public int getIconWidth() {
 438         return width;
 439     }
 440 
 441     /**
 442      * Gets the height of the icon.
 443      *
 444      * @return the height in pixels of this icon
 445      */
 446     public int getIconHeight() {
 447         return height;
 448     }
 449 
 450     /**
 451      * Sets the image observer for the image.  Set this
 452      * property if the ImageIcon contains an animated GIF, so
 453      * the observer is notified to update its display.
 454      * For example:
 455      * <pre>
 456      *     icon = new ImageIcon(...)
 457      *     button.setIcon(icon);
 458      *     icon.setImageObserver(button);
 459      * </pre>
 460      *
 461      * @param observer the image observer
 462      */
 463     public void setImageObserver(ImageObserver observer) {
 464         imageObserver = observer;
 465     }
 466 
 467     /**
 468      * Returns the image observer for the image.
 469      *
 470      * @return the image observer, which may be null
 471      */
 472     @Transient
 473     public ImageObserver getImageObserver() {
 474         return imageObserver;
 475     }
 476 
 477     /**
 478      * Returns a string representation of this image.
 479      *
 480      * @return a string representing this image
 481      */
 482     public String toString() {
 483         if (description != null) {
 484             return description;
 485         }
 486         return super.toString();
 487     }
 488 
 489     private void readObject(ObjectInputStream s)
 490         throws ClassNotFoundException, IOException
 491     {
 492         s.defaultReadObject();
 493 
 494         int w = s.readInt();
 495         int h = s.readInt();
 496         int[] pixels = (int[])(s.readObject());
 497 
 498         if (pixels != null) {
 499             Toolkit tk = Toolkit.getDefaultToolkit();
 500             ColorModel cm = ColorModel.getRGBdefault();
 501             image = tk.createImage(new MemoryImageSource(w, h, cm, pixels, 0, w));
 502             loadImage(image);
 503         }
 504     }
 505 
 506 
 507     private void writeObject(ObjectOutputStream s)
 508         throws IOException
 509     {
 510         s.defaultWriteObject();
 511 
 512         int w = getIconWidth();
 513         int h = getIconHeight();
 514         int[] pixels = image != null? new int[w * h] : null;
 515 
 516         if (image != null) {
 517             try {
 518                 PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
 519                 pg.grabPixels();
 520                 if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
 521                     throw new IOException("failed to load image contents");
 522                 }
 523             }
 524             catch (InterruptedException e) {
 525                 throw new IOException("image load interrupted");
 526             }
 527         }
 528 
 529         s.writeInt(w);
 530         s.writeInt(h);
 531         s.writeObject(pixels);
 532     }
 533 
 534     /**
 535      * --- Accessibility Support ---
 536      */
 537 
 538     private AccessibleImageIcon accessibleContext = null;
 539 
 540     /**
 541      * Gets the AccessibleContext associated with this ImageIcon.
 542      * For image icons, the AccessibleContext takes the form of an
 543      * AccessibleImageIcon.
 544      * A new AccessibleImageIcon instance is created if necessary.
 545      *
 546      * @return an AccessibleImageIcon that serves as the
 547      *         AccessibleContext of this ImageIcon
 548      * @beaninfo
 549      *       expert: true
 550      *  description: The AccessibleContext associated with this ImageIcon.
 551      * @since 1.3
 552      */
 553     public AccessibleContext getAccessibleContext() {
 554         if (accessibleContext == null) {
 555             accessibleContext = new AccessibleImageIcon();
 556         }
 557         return accessibleContext;
 558     }
 559 
 560     /**
 561      * This class implements accessibility support for the
 562      * <code>ImageIcon</code> class.  It provides an implementation of the
 563      * Java Accessibility API appropriate to image icon user-interface
 564      * elements.
 565      * <p>
 566      * <strong>Warning:</strong>
 567      * Serialized objects of this class will not be compatible with
 568      * future Swing releases. The current serialization support is
 569      * appropriate for short term storage or RMI between applications running
 570      * the same version of Swing.  As of 1.4, support for long term storage
 571      * of all JavaBeans&trade;
 572      * has been added to the <code>java.beans</code> package.
 573      * Please see {@link java.beans.XMLEncoder}.
 574      * @since 1.3
 575      */
 576     @SuppressWarnings("serial") // Same-version serialization only
 577     protected class AccessibleImageIcon extends AccessibleContext
 578         implements AccessibleIcon, Serializable {
 579 
 580         /*
 581          * AccessibleContest implementation -----------------
 582          */
 583 
 584         /**
 585          * Gets the role of this object.
 586          *
 587          * @return an instance of AccessibleRole describing the role of the
 588          * object
 589          * @see AccessibleRole
 590          */
 591         public AccessibleRole getAccessibleRole() {
 592             return AccessibleRole.ICON;
 593         }
 594 
 595         /**
 596          * Gets the state of this object.
 597          *
 598          * @return an instance of AccessibleStateSet containing the current
 599          * state set of the object
 600          * @see AccessibleState
 601          */
 602         public AccessibleStateSet getAccessibleStateSet() {
 603             return null;
 604         }
 605 
 606         /**
 607          * Gets the Accessible parent of this object.  If the parent of this
 608          * object implements Accessible, this method should simply return
 609          * getParent().
 610          *
 611          * @return the Accessible parent of this object -- can be null if this
 612          * object does not have an Accessible parent
 613          */
 614         public Accessible getAccessibleParent() {
 615             return null;
 616         }
 617 
 618         /**
 619          * Gets the index of this object in its accessible parent.
 620          *
 621          * @return the index of this object in its parent; -1 if this
 622          * object does not have an accessible parent.
 623          * @see #getAccessibleParent
 624          */
 625         public int getAccessibleIndexInParent() {
 626             return -1;
 627         }
 628 
 629         /**
 630          * Returns the number of accessible children in the object.  If all
 631          * of the children of this object implement Accessible, than this
 632          * method should return the number of children of this object.
 633          *
 634          * @return the number of accessible children in the object.
 635          */
 636         public int getAccessibleChildrenCount() {
 637             return 0;
 638         }
 639 
 640         /**
 641          * Returns the nth Accessible child of the object.
 642          *
 643          * @param i zero-based index of child
 644          * @return the nth Accessible child of the object
 645          */
 646         public Accessible getAccessibleChild(int i) {
 647             return null;
 648         }
 649 
 650         /**
 651          * Returns the locale of this object.
 652          *
 653          * @return the locale of this object
 654          */
 655         public Locale getLocale() throws IllegalComponentStateException {
 656             return null;
 657         }
 658 
 659         /*
 660          * AccessibleIcon implementation -----------------
 661          */
 662 
 663         /**
 664          * Gets the description of the icon.  This is meant to be a brief
 665          * textual description of the object.  For example, it might be
 666          * presented to a blind user to give an indication of the purpose
 667          * of the icon.
 668          *
 669          * @return the description of the icon
 670          */
 671         public String getAccessibleIconDescription() {
 672             return ImageIcon.this.getDescription();
 673         }
 674 
 675         /**
 676          * Sets the description of the icon.  This is meant to be a brief
 677          * textual description of the object.  For example, it might be
 678          * presented to a blind user to give an indication of the purpose
 679          * of the icon.
 680          *
 681          * @param description the description of the icon
 682          */
 683         public void setAccessibleIconDescription(String description) {
 684             ImageIcon.this.setDescription(description);
 685         }
 686 
 687         /**
 688          * Gets the height of the icon.
 689          *
 690          * @return the height of the icon
 691          */
 692         public int getAccessibleIconHeight() {
 693             return ImageIcon.this.height;
 694         }
 695 
 696         /**
 697          * Gets the width of the icon.
 698          *
 699          * @return the width of the icon
 700          */
 701         public int getAccessibleIconWidth() {
 702             return ImageIcon.this.width;
 703         }
 704 
 705         private void readObject(ObjectInputStream s)
 706             throws ClassNotFoundException, IOException
 707         {
 708             s.defaultReadObject();
 709         }
 710 
 711         private void writeObject(ObjectOutputStream s)
 712             throws IOException
 713         {
 714             s.defaultWriteObject();
 715         }
 716     }  // AccessibleImageIcon
 717 }