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 */ 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™ 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 }