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