001 /* BufferedImage.java --
002 Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, Free Software Foundation
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt.image;
040
041 import gnu.java.awt.Buffers;
042 import gnu.java.awt.ClasspathGraphicsEnvironment;
043 import gnu.java.awt.ComponentDataBlitOp;
044 import gnu.java.lang.CPStringBuilder;
045
046 import java.awt.Graphics;
047 import java.awt.Graphics2D;
048 import java.awt.GraphicsEnvironment;
049 import java.awt.Image;
050 import java.awt.Point;
051 import java.awt.Rectangle;
052 import java.awt.Transparency;
053 import java.awt.color.ColorSpace;
054 import java.util.Hashtable;
055 import 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 */
067 public 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 }