001/* BufferedImage.java -- 002 Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, Free Software Foundation 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.awt.image; 040 041import gnu.java.awt.Buffers; 042import gnu.java.awt.ClasspathGraphicsEnvironment; 043import gnu.java.awt.ComponentDataBlitOp; 044import gnu.java.lang.CPStringBuilder; 045 046import java.awt.Graphics; 047import java.awt.Graphics2D; 048import java.awt.GraphicsEnvironment; 049import java.awt.Image; 050import java.awt.Point; 051import java.awt.Rectangle; 052import java.awt.Transparency; 053import java.awt.color.ColorSpace; 054import java.util.Hashtable; 055import java.util.Vector; 056 057/** 058 * A buffered image always starts at coordinates (0, 0). 059 * 060 * The buffered image is not subdivided into multiple tiles. Instead, 061 * the image consists of one large tile (0,0) with the width and 062 * height of the image. This tile is always considered to be checked 063 * out. 064 * 065 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 066 */ 067public class BufferedImage extends Image 068 implements WritableRenderedImage, Transparency 069{ 070 public static final int TYPE_CUSTOM = 0, 071 TYPE_INT_RGB = 1, 072 TYPE_INT_ARGB = 2, 073 TYPE_INT_ARGB_PRE = 3, 074 TYPE_INT_BGR = 4, 075 TYPE_3BYTE_BGR = 5, 076 TYPE_4BYTE_ABGR = 6, 077 TYPE_4BYTE_ABGR_PRE = 7, 078 TYPE_USHORT_565_RGB = 8, 079 TYPE_USHORT_555_RGB = 9, 080 TYPE_BYTE_GRAY = 10, 081 TYPE_USHORT_GRAY = 11, 082 TYPE_BYTE_BINARY = 12, 083 TYPE_BYTE_INDEXED = 13; 084 085 /** 086 * Vector of TileObservers (or null) 087 */ 088 Vector<TileObserver> tileObservers; 089 090 /** 091 * The image's WritableRaster 092 */ 093 WritableRaster raster; 094 095 /** 096 * The associated ColorModel 097 */ 098 ColorModel colorModel; 099 100 /** 101 * The image's properties (or null) 102 */ 103 Hashtable properties; 104 105 /** 106 * Whether alpha is premultiplied 107 */ 108 boolean isPremultiplied; 109 110 /** 111 * The predefined type, if any. 112 */ 113 int type; 114 115 /** 116 * Creates a new <code>BufferedImage</code> with the specified width, height 117 * and type. Valid <code>type</code> values are: 118 * 119 * <ul> 120 * <li>{@link #TYPE_INT_RGB}</li> 121 * <li>{@link #TYPE_INT_ARGB}</li> 122 * <li>{@link #TYPE_INT_ARGB_PRE}</li> 123 * <li>{@link #TYPE_INT_BGR}</li> 124 * <li>{@link #TYPE_3BYTE_BGR}</li> 125 * <li>{@link #TYPE_4BYTE_ABGR}</li> 126 * <li>{@link #TYPE_4BYTE_ABGR_PRE}</li> 127 * <li>{@link #TYPE_USHORT_565_RGB}</li> 128 * <li>{@link #TYPE_USHORT_555_RGB}</li> 129 * <li>{@link #TYPE_BYTE_GRAY}</li> 130 * <li>{@link #TYPE_USHORT_GRAY}</li> 131 * <li>{@link #TYPE_BYTE_BINARY}</li> 132 * <li>{@link #TYPE_BYTE_INDEXED}</li> 133 * </ul> 134 * 135 * @param width the width (must be > 0). 136 * @param height the height (must be > 0). 137 * @param type the image type (see the list of valid types above). 138 * 139 * @throws IllegalArgumentException if <code>width</code> or 140 * <code>height</code> is less than or equal to zero. 141 * @throws IllegalArgumentException if <code>type</code> is not one of the 142 * specified values. 143 */ 144 public BufferedImage(int width, int height, int type) 145 { 146 SampleModel sm = null; 147 ColorModel cm = null; 148 boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE 149 || type == BufferedImage.TYPE_4BYTE_ABGR_PRE); 150 151 switch( type ) 152 { 153 case BufferedImage.TYPE_INT_RGB: 154 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 155 width, height, 156 new int[]{ 0x00FF0000, 157 0x0000FF00, 158 0x000000FF } ) ; 159 cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff ); 160 break; 161 162 case BufferedImage.TYPE_3BYTE_BGR: 163 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, 164 width, height, 165 3, width * 3, 166 new int[]{ 2, 1, 0 } ); 167 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 168 false, false, 169 BufferedImage.OPAQUE, 170 DataBuffer.TYPE_BYTE); 171 break; 172 173 case BufferedImage.TYPE_INT_ARGB: 174 case BufferedImage.TYPE_INT_ARGB_PRE: 175 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 176 width, height, 177 new int[]{ 0x00FF0000, 178 0x0000FF00, 179 0x000000FF, 180 0xFF000000 } ); 181 if (premultiplied) 182 cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), 183 32, 0xff0000, 0xff00, 0xff, 0xff000000, 184 true, 185 Buffers.smallestAppropriateTransferType(32)); 186 else 187 cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 ); 188 189 break; 190 191 case BufferedImage.TYPE_4BYTE_ABGR: 192 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 193 sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 194 width, height, 195 4, 4*width, 196 new int[]{3, 2, 1, 0}); 197 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 198 true, premultiplied, 199 BufferedImage.TRANSLUCENT, 200 DataBuffer.TYPE_BYTE); 201 break; 202 203 case BufferedImage.TYPE_INT_BGR: 204 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 205 width, height, 206 new int[]{ 0x000000FF, 207 0x0000FF00, 208 0x00FF0000 } ) ; 209 cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 ); 210 break; 211 212 case BufferedImage.TYPE_USHORT_565_RGB: 213 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, 214 width, height, 215 new int[]{ 0xF800, 216 0x7E0, 217 0x1F } ) ; 218 cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F ); 219 break; 220 221 case BufferedImage.TYPE_USHORT_555_RGB: 222 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, 223 width, height, 224 new int[]{ 0x7C00, 225 0x3E0, 226 0x1F } ) ; 227 cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F ); 228 break; 229 230 case BufferedImage.TYPE_BYTE_INDEXED: 231 cm = createDefaultIndexedColorModel( false ); 232 233 case BufferedImage.TYPE_BYTE_GRAY: 234 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, 235 width, height, 236 1, width, new int[]{ 0 } ); 237 break; 238 239 case BufferedImage.TYPE_USHORT_GRAY: 240 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT, 241 width, height, 242 1, width, new int[]{ 0 } ); 243 break; 244 245 case BufferedImage.TYPE_BYTE_BINARY: 246 cm = createDefaultIndexedColorModel( true ); 247 sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 248 width, height, 1); 249 break; 250 251 default: 252 sm = null; 253 } 254 255 if( sm == null ) 256 throw new IllegalArgumentException("Unknown predefined image type."); 257 258 if( cm == null ) // only for the grayscale types 259 { 260 int buftype; 261 int[] bits = new int[1]; 262 if( type == BufferedImage.TYPE_BYTE_GRAY ) 263 { 264 buftype = DataBuffer.TYPE_BYTE; 265 bits[0] = 8; 266 } 267 else 268 { 269 buftype = DataBuffer.TYPE_USHORT; 270 bits[0] = 16; 271 } 272 ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY ); 273 274 cm = new ComponentColorModel( graySpace, bits, false, false, 275 Transparency.OPAQUE, buftype ); 276 } 277 278 WritableRaster rst = null; 279 280 // Attempt to create an accelerated backend for this image 281 GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); 282 if (env instanceof ClasspathGraphicsEnvironment) 283 rst = ((ClasspathGraphicsEnvironment)env).createRaster(cm, sm); 284 285 // Default to a standard Java raster & databuffer if needed 286 if (rst == null) 287 rst = Raster.createWritableRaster(sm, new Point( 0, 0 ) ); 288 289 init(cm, rst, premultiplied, 290 null, // no properties 291 type ); 292 } 293 294 public BufferedImage(int w, int h, int type, IndexColorModel indexcolormodel) 295 { 296 if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED)) 297 throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED"); 298 if( indexcolormodel.getMapSize() > 16 && type == TYPE_BYTE_BINARY ) 299 throw new IllegalArgumentException("Type TYPE_BYTE_BINARY cannot have a larger than 16-color palette."); 300 if( indexcolormodel.getMapSize() > 256 ) 301 throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette."); 302 303 init( indexcolormodel, 304 indexcolormodel.createCompatibleWritableRaster(w, h), 305 indexcolormodel.isAlphaPremultiplied(), 306 null, // no properties 307 type ); 308 } 309 310 public BufferedImage(ColorModel colormodel, WritableRaster writableraster, 311 boolean premultiplied, Hashtable<?,?> properties) 312 { 313 init(colormodel, writableraster, premultiplied, properties, TYPE_CUSTOM); 314 } 315 316 317 private void init(ColorModel cm, WritableRaster writableraster, 318 boolean premultiplied, Hashtable properties, int type) 319 { 320 raster = writableraster; 321 colorModel = cm; 322 this.properties = properties; 323 isPremultiplied = premultiplied; 324 this.type = type; 325 } 326 327 /** 328 * Creates the default palettes for the predefined indexed color types 329 * (256-color or black-and-white) 330 * 331 * @param binary - If <code>true</code>, a black and white palette, 332 * otherwise a default 256-color palette is returned. 333 */ 334 private IndexColorModel createDefaultIndexedColorModel( boolean binary ) 335 { 336 if( binary ) 337 { 338 byte[] t = new byte[]{ 0, (byte)255 }; 339 return new IndexColorModel( 1, 2, t, t, t ); 340 } 341 342 byte[] r = new byte[256]; 343 byte[] g = new byte[256]; 344 byte[] b = new byte[256]; 345 346 int index = 0; 347 for( int i = 0; i < 6; i++ ) 348 for( int j = 0; j < 6; j++ ) 349 for( int k = 0; k < 6; k++ ) 350 { 351 r[ index ] = (byte)(i * 51); 352 g[ index ] = (byte)(j * 51); 353 b[ index ] = (byte)(k * 51); 354 index++; 355 } 356 357 while( index < 256 ) 358 { 359 r[ index ] = g[ index ] = b[ index ] = 360 (byte)(18 + (index - 216) * 6); 361 index++; 362 } 363 364 return new IndexColorModel( 8, 256, r, g, b ); 365 } 366 367 public void coerceData(boolean premultiplied) 368 { 369 colorModel = colorModel.coerceData(raster, premultiplied); 370 isPremultiplied = premultiplied; 371 } 372 373 public WritableRaster copyData(WritableRaster dest) 374 { 375 if (dest == null) 376 dest = raster.createCompatibleWritableRaster(getMinX(), getMinY(), 377 getWidth(),getHeight()); 378 379 int x = dest.getMinX(); 380 int y = dest.getMinY(); 381 int w = dest.getWidth(); 382 int h = dest.getHeight(); 383 384 // create a src child that has the right bounds... 385 WritableRaster src = 386 raster.createWritableChild(x, y, w, h, x, y, 387 null); // same bands 388 389 if (src.getSampleModel () instanceof ComponentSampleModel 390 && dest.getSampleModel () instanceof ComponentSampleModel) 391 // Refer to ComponentDataBlitOp for optimized data blitting: 392 ComponentDataBlitOp.INSTANCE.filter(src, dest); 393 394 else 395 { 396 // slower path 397 int samples[] = src.getPixels (x, y, w, h, (int [])null); 398 dest.setPixels (x, y, w, h, samples); 399 } 400 return dest; 401 } 402 403 public Graphics2D createGraphics() 404 { 405 GraphicsEnvironment env; 406 env = GraphicsEnvironment.getLocalGraphicsEnvironment (); 407 return env.createGraphics (this); 408 } 409 410 public void flush() 411 { 412 } 413 414 public WritableRaster getAlphaRaster() 415 { 416 return colorModel.getAlphaRaster(raster); 417 } 418 419 public ColorModel getColorModel() 420 { 421 return colorModel; 422 } 423 424 public Raster getData() 425 { 426 return copyData(null); 427 /* TODO: this might be optimized by returning the same 428 raster (not writable) as long as image data doesn't change. */ 429 } 430 431 public Raster getData(Rectangle rectangle) 432 { 433 WritableRaster dest = 434 raster.createCompatibleWritableRaster(rectangle); 435 return copyData(dest); 436 } 437 438 public Graphics getGraphics() 439 { 440 return createGraphics(); 441 } 442 443 public int getHeight() 444 { 445 return raster.getHeight(); 446 } 447 448 public int getHeight(ImageObserver imageobserver) 449 { 450 return getHeight(); 451 } 452 453 public int getMinTileX() 454 { 455 return 0; 456 } 457 458 public int getMinTileY() 459 { 460 return 0; 461 } 462 463 public int getMinX() 464 { 465 return 0; 466 } 467 468 public int getMinY() 469 { 470 return 0; 471 } 472 473 public int getNumXTiles() 474 { 475 return 1; 476 } 477 478 public int getNumYTiles() 479 { 480 return 1; 481 } 482 483 /** 484 * Returns the value of the specified property, or 485 * {@link Image#UndefinedProperty} if the property is not defined. 486 * 487 * @param string the property key (<code>null</code> not permitted). 488 * 489 * @return The property value. 490 * 491 * @throws NullPointerException if <code>string</code> is <code>null</code>. 492 */ 493 public Object getProperty(String string) 494 { 495 if (string == null) 496 throw new NullPointerException("The property name cannot be null."); 497 Object result = Image.UndefinedProperty; 498 if (properties != null) 499 { 500 Object v = properties.get(string); 501 if (v != null) 502 result = v; 503 } 504 return result; 505 } 506 507 public Object getProperty(String string, ImageObserver imageobserver) 508 { 509 return getProperty(string); 510 } 511 512 /** 513 * Returns <code>null</code> always. 514 * 515 * @return <code>null</code> always. 516 */ 517 public String[] getPropertyNames() 518 { 519 // This method should always return null, see: 520 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4640609 521 return null; 522 } 523 524 public int getRGB(int x, int y) 525 { 526 Object rgbElem = raster.getDataElements(x, y, null); 527 return colorModel.getRGB(rgbElem); 528 } 529 530 public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, 531 int offset, int scanlineStride) 532 { 533 if (rgbArray == null) 534 { 535 /* 536 000000000000000000 537 00000[#######----- [ = start 538 -----########----- ] = end 539 -----#######]00000 540 000000000000000000 541 */ 542 int size = (h-1)*scanlineStride + w; 543 rgbArray = new int[size]; 544 } 545 546 int endX = startX + w; 547 int endY = startY + h; 548 549 /* *TODO*: 550 Opportunity for optimization by examining color models... 551 552 Perhaps wrap the rgbArray up in a WritableRaster with packed 553 sRGB color model and perform optimized rendering into the 554 array. */ 555 556 Object rgbElem = null; 557 for (int y=startY; y<endY; y++) 558 { 559 int xoffset = offset; 560 for (int x=startX; x<endX; x++) 561 { 562 int rgb; 563 rgbElem = raster.getDataElements(x, y, rgbElem); 564 rgb = colorModel.getRGB(rgbElem); 565 rgbArray[xoffset++] = rgb; 566 } 567 offset += scanlineStride; 568 } 569 return rgbArray; 570 } 571 572 public WritableRaster getRaster() 573 { 574 return raster; 575 } 576 577 public SampleModel getSampleModel() 578 { 579 return raster.getSampleModel(); 580 } 581 582 public ImageProducer getSource() 583 { 584 return new ImageProducer() 585 { 586 Vector<ImageConsumer> consumers = new Vector<ImageConsumer>(); 587 588 public void addConsumer(ImageConsumer ic) 589 { 590 if(!consumers.contains(ic)) 591 consumers.add(ic); 592 } 593 594 public boolean isConsumer(ImageConsumer ic) 595 { 596 return consumers.contains(ic); 597 } 598 599 public void removeConsumer(ImageConsumer ic) 600 { 601 consumers.remove(ic); 602 } 603 604 public void startProduction(ImageConsumer ic) 605 { 606 int x = 0; 607 int y = 0; 608 int width = getWidth(); 609 int height = getHeight(); 610 int stride = width; 611 int offset = 0; 612 int[] pixels = getRGB(x, y, 613 width, height, 614 (int[])null, offset, stride); 615 // We already convert the color to RGB in the getRGB call, so 616 // we pass a simple RGB color model to the consumers. 617 ColorModel model = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 618 0xff000000); 619 620 consumers.add(ic); 621 622 for(int i = 0; i < consumers.size(); i++) 623 { 624 ImageConsumer c = consumers.elementAt(i); 625 c.setHints(ImageConsumer.SINGLEPASS); 626 c.setDimensions(getWidth(), getHeight()); 627 c.setPixels(x, y, width, height, model, pixels, offset, stride); 628 c.imageComplete(ImageConsumer.STATICIMAGEDONE); 629 } 630 } 631 632 public void requestTopDownLeftRightResend(ImageConsumer ic) 633 { 634 startProduction(ic); 635 } 636 637 }; 638 } 639 640 public Vector<RenderedImage> getSources() 641 { 642 return null; 643 } 644 645 public BufferedImage getSubimage(int x, int y, int w, int h) 646 { 647 WritableRaster subRaster = 648 getRaster().createWritableChild(x, y, w, h, 0, 0, null); 649 650 return new BufferedImage(getColorModel(), subRaster, isPremultiplied, 651 properties); 652 } 653 654 public Raster getTile(int tileX, int tileY) 655 { 656 return getWritableTile(tileX, tileY); 657 } 658 659 public int getTileGridXOffset() 660 { 661 return 0; // according to javadocs 662 } 663 664 public int getTileGridYOffset() 665 { 666 return 0; // according to javadocs 667 } 668 669 public int getTileHeight() 670 { 671 return getHeight(); // image is one big tile 672 } 673 674 public int getTileWidth() 675 { 676 return getWidth(); // image is one big tile 677 } 678 679 public int getType() 680 { 681 return type; 682 } 683 684 public int getWidth() 685 { 686 return raster.getWidth(); 687 } 688 689 public int getWidth(ImageObserver imageobserver) 690 { 691 return getWidth(); 692 } 693 694 public WritableRaster getWritableTile(int tileX, int tileY) 695 { 696 isTileWritable(tileX, tileY); // for exception 697 return raster; 698 } 699 700 private static final Point[] tileIndices = { new Point() }; 701 702 public Point[] getWritableTileIndices() 703 { 704 return tileIndices; 705 } 706 707 public boolean hasTileWriters() 708 { 709 return true; 710 } 711 712 public boolean isAlphaPremultiplied() 713 { 714 return isPremultiplied; 715 } 716 717 public boolean isTileWritable(int tileX, int tileY) 718 { 719 if ((tileX != 0) || (tileY != 0)) 720 throw new ArrayIndexOutOfBoundsException("only tile is (0,0)"); 721 return true; 722 } 723 724 public void releaseWritableTile(int tileX, int tileY) 725 { 726 isTileWritable(tileX, tileY); // for exception 727 } 728 729 //public void removeTileObserver(TileObserver tileobserver) {} 730 731 public void setData(Raster src) 732 { 733 int x = src.getMinX(); 734 int y = src.getMinY(); 735 int w = src.getWidth(); 736 int h = src.getHeight(); 737 738 // create a dest child that has the right bounds... 739 WritableRaster dest = 740 raster.createWritableChild(x, y, w, h, x, y, null); 741 742 if (src.getSampleModel () instanceof ComponentSampleModel 743 && dest.getSampleModel () instanceof ComponentSampleModel) 744 745 // Refer to ComponentDataBlitOp for optimized data blitting: 746 ComponentDataBlitOp.INSTANCE.filter(src, dest); 747 else 748 { 749 // slower path 750 int samples[] = src.getPixels (x, y, w, h, (int [])null); 751 dest.setPixels (x, y, w, h, samples); 752 } 753 } 754 755 public void setRGB(int x, int y, int argb) 756 { 757 Object rgbElem = colorModel.getDataElements(argb, null); 758 raster.setDataElements(x, y, rgbElem); 759 } 760 761 public void setRGB(int startX, int startY, int w, int h, 762 int[] argbArray, int offset, int scanlineStride) 763 { 764 int endX = startX + w; 765 int endY = startY + h; 766 767 Object rgbElem = null; 768 for (int y=startY; y<endY; y++) 769 { 770 int xoffset = offset; 771 for (int x=startX; x<endX; x++) 772 { 773 int argb = argbArray[xoffset++]; 774 rgbElem = colorModel.getDataElements(argb, rgbElem); 775 raster.setDataElements(x, y, rgbElem); 776 } 777 offset += scanlineStride; 778 } 779 } 780 781 public String toString() 782 { 783 CPStringBuilder buf; 784 785 buf = new CPStringBuilder(/* estimated length */ 120); 786 buf.append("BufferedImage@"); 787 buf.append(Integer.toHexString(hashCode())); 788 buf.append(": type="); 789 buf.append(type); 790 buf.append(' '); 791 buf.append(colorModel); 792 buf.append(' '); 793 buf.append(raster); 794 795 return buf.toString(); 796 } 797 798 799 /** 800 * Adds a tile observer. If the observer is already present, it receives 801 * multiple notifications. 802 * 803 * @param to The TileObserver to add. 804 */ 805 public void addTileObserver (TileObserver to) 806 { 807 if (tileObservers == null) 808 tileObservers = new Vector<TileObserver>(); 809 810 tileObservers.add (to); 811 } 812 813 /** 814 * Removes a tile observer. If the observer was not registered, 815 * nothing happens. If the observer was registered for multiple 816 * notifications, it is now registered for one fewer notification. 817 * 818 * @param to The TileObserver to remove. 819 */ 820 public void removeTileObserver (TileObserver to) 821 { 822 if (tileObservers == null) 823 return; 824 825 tileObservers.remove (to); 826 } 827 828 /** 829 * Return the transparency type. 830 * 831 * @return One of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT}. 832 * @see Transparency#getTransparency() 833 * @since 1.5 834 */ 835 public int getTransparency() 836 { 837 return colorModel.getTransparency(); 838 } 839}