1 /* BufferedImage.java --
2 Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.awt.image;
41 import gnu.java.awt.Buffers;
42 import gnu.java.awt.ClasspathGraphicsEnvironment;
43 import gnu.java.awt.ComponentDataBlitOp;
44 import gnu.java.awt.peer.gtk.CairoSurface;
46 import java.awt.Graphics;
47 import java.awt.Graphics2D;
48 import java.awt.GraphicsEnvironment;
49 import java.awt.Image;
50 import java.awt.Point;
51 import java.awt.Rectangle;
52 import java.awt.Transparency;
53 import java.awt.color.ColorSpace;
54 import java.util.Hashtable;
55 import java.util.Vector;
58 * A buffered image always starts at coordinates (0, 0).
60 * The buffered image is not subdivided into multiple tiles. Instead,
61 * the image consists of one large tile (0,0) with the width and
62 * height of the image. This tile is always considered to be checked
65 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
67 public class BufferedImage extends Image
68 implements WritableRenderedImage, Transparency
70 public static final int TYPE_CUSTOM = 0,
73 TYPE_INT_ARGB_PRE = 3,
77 TYPE_4BYTE_ABGR_PRE = 7,
78 TYPE_USHORT_565_RGB = 8,
79 TYPE_USHORT_555_RGB = 9,
81 TYPE_USHORT_GRAY = 11,
82 TYPE_BYTE_BINARY = 12,
83 TYPE_BYTE_INDEXED = 13;
86 * Vector of TileObservers (or null)
88 Vector<TileObserver> tileObservers;
91 * The image's WritableRaster
93 WritableRaster raster;
96 * The associated ColorModel
98 ColorModel colorModel;
101 * The image's properties (or null)
103 Hashtable properties;
106 * Whether alpha is premultiplied
108 boolean isPremultiplied;
111 * The predefined type, if any.
116 * Creates a new <code>BufferedImage</code> with the specified width, height
117 * and type. Valid <code>type</code> values are:
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>
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).
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
144 public BufferedImage(int width, int height, int type)
146 SampleModel sm = null;
147 ColorModel cm = null;
148 boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE
149 || type == BufferedImage.TYPE_4BYTE_ABGR_PRE);
153 case BufferedImage.TYPE_INT_RGB:
154 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
156 new int[]{ 0x00FF0000,
159 cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff );
162 case BufferedImage.TYPE_3BYTE_BGR:
163 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
166 new int[]{ 2, 1, 0 } );
167 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
169 BufferedImage.OPAQUE,
170 DataBuffer.TYPE_BYTE);
173 case BufferedImage.TYPE_INT_ARGB:
174 case BufferedImage.TYPE_INT_ARGB_PRE:
175 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
177 new int[]{ 0x00FF0000,
182 cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
183 32, 0xff0000, 0xff00, 0xff, 0xff000000,
185 Buffers.smallestAppropriateTransferType(32));
187 cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
191 case BufferedImage.TYPE_4BYTE_ABGR:
192 case BufferedImage.TYPE_4BYTE_ABGR_PRE:
193 sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
196 new int[]{3, 2, 1, 0});
197 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
199 BufferedImage.TRANSLUCENT,
200 DataBuffer.TYPE_BYTE);
203 case BufferedImage.TYPE_INT_BGR:
204 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
206 new int[]{ 0x000000FF,
209 cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 );
212 case BufferedImage.TYPE_USHORT_565_RGB:
213 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
218 cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F );
221 case BufferedImage.TYPE_USHORT_555_RGB:
222 sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
227 cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F );
230 case BufferedImage.TYPE_BYTE_INDEXED:
231 cm = createDefaultIndexedColorModel( false );
233 case BufferedImage.TYPE_BYTE_GRAY:
234 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
236 1, width, new int[]{ 0 } );
239 case BufferedImage.TYPE_USHORT_GRAY:
240 sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT,
242 1, width, new int[]{ 0 } );
245 case BufferedImage.TYPE_BYTE_BINARY:
246 cm = createDefaultIndexedColorModel( true );
247 sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
256 throw new IllegalArgumentException("Unknown predefined image type.");
258 if( cm == null ) // only for the grayscale types
261 int[] bits = new int[1];
262 if( type == BufferedImage.TYPE_BYTE_GRAY )
264 buftype = DataBuffer.TYPE_BYTE;
269 buftype = DataBuffer.TYPE_USHORT;
272 ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY );
274 cm = new ComponentColorModel( graySpace, bits, false, false,
275 Transparency.OPAQUE, buftype );
278 WritableRaster rst = null;
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);
285 // Default to a standard Java raster & databuffer if needed
287 rst = Raster.createWritableRaster(sm, new Point( 0, 0 ) );
289 init(cm, rst, premultiplied,
290 null, // no properties
294 public BufferedImage(int w, int h, int type, IndexColorModel indexcolormodel)
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.");
303 init( indexcolormodel,
304 indexcolormodel.createCompatibleWritableRaster(w, h),
305 indexcolormodel.isAlphaPremultiplied(),
306 null, // no properties
310 public BufferedImage(ColorModel colormodel, WritableRaster writableraster,
311 boolean premultiplied, Hashtable<?,?> properties)
313 init(colormodel, writableraster, premultiplied, properties, TYPE_CUSTOM);
317 private void init(ColorModel cm, WritableRaster writableraster,
318 boolean premultiplied, Hashtable properties, int type)
320 raster = writableraster;
322 this.properties = properties;
323 isPremultiplied = premultiplied;
328 * Creates the default palettes for the predefined indexed color types
329 * (256-color or black-and-white)
331 * @param binary - If <code>true</code>, a black and white palette,
332 * otherwise a default 256-color palette is returned.
334 private IndexColorModel createDefaultIndexedColorModel( boolean binary )
338 byte[] t = new byte[]{ 0, (byte)255 };
339 return new IndexColorModel( 1, 2, t, t, t );
342 byte[] r = new byte[256];
343 byte[] g = new byte[256];
344 byte[] b = new byte[256];
347 for( int i = 0; i < 6; i++ )
348 for( int j = 0; j < 6; j++ )
349 for( int k = 0; k < 6; k++ )
351 r[ index ] = (byte)(i * 51);
352 g[ index ] = (byte)(j * 51);
353 b[ index ] = (byte)(k * 51);
359 r[ index ] = g[ index ] = b[ index ] =
360 (byte)(18 + (index - 216) * 6);
364 return new IndexColorModel( 8, 256, r, g, b );
367 public void coerceData(boolean premultiplied)
369 colorModel = colorModel.coerceData(raster, premultiplied);
370 isPremultiplied = premultiplied;
373 public WritableRaster copyData(WritableRaster dest)
376 dest = raster.createCompatibleWritableRaster(getMinX(), getMinY(),
377 getWidth(),getHeight());
379 int x = dest.getMinX();
380 int y = dest.getMinY();
381 int w = dest.getWidth();
382 int h = dest.getHeight();
384 // create a src child that has the right bounds...
386 raster.createWritableChild(x, y, w, h, x, y,
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);
397 int samples[] = src.getPixels (x, y, w, h, (int [])null);
398 dest.setPixels (x, y, w, h, samples);
403 public Graphics2D createGraphics()
405 GraphicsEnvironment env;
406 env = GraphicsEnvironment.getLocalGraphicsEnvironment ();
407 return env.createGraphics (this);
414 public WritableRaster getAlphaRaster()
416 return colorModel.getAlphaRaster(raster);
419 public ColorModel getColorModel()
424 public Raster getData()
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. */
431 public Raster getData(Rectangle rectangle)
433 WritableRaster dest =
434 raster.createCompatibleWritableRaster(rectangle);
435 return copyData(dest);
438 public Graphics getGraphics()
440 return createGraphics();
443 public int getHeight()
445 return raster.getHeight();
448 public int getHeight(ImageObserver imageobserver)
453 public int getMinTileX()
458 public int getMinTileY()
473 public int getNumXTiles()
478 public int getNumYTiles()
484 * Returns the value of the specified property, or
485 * {@link Image#UndefinedProperty} if the property is not defined.
487 * @param string the property key (<code>null</code> not permitted).
489 * @return The property value.
491 * @throws NullPointerException if <code>string</code> is <code>null</code>.
493 public Object getProperty(String string)
496 throw new NullPointerException("The property name cannot be null.");
497 Object result = Image.UndefinedProperty;
498 if (properties != null)
500 Object v = properties.get(string);
507 public Object getProperty(String string, ImageObserver imageobserver)
509 return getProperty(string);
513 * Returns <code>null</code> always.
515 * @return <code>null</code> always.
517 public String[] getPropertyNames()
519 // This method should always return null, see:
520 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4640609
524 public int getRGB(int x, int y)
526 Object rgbElem = raster.getDataElements(x, y, null);
527 return colorModel.getRGB(rgbElem);
530 public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray,
531 int offset, int scanlineStride)
533 if (rgbArray == null)
537 00000[#######----- [ = start
538 -----########----- ] = end
542 int size = (h-1)*scanlineStride + w;
543 rgbArray = new int[size];
546 int endX = startX + w;
547 int endY = startY + h;
550 Opportunity for optimization by examining color models...
552 Perhaps wrap the rgbArray up in a WritableRaster with packed
553 sRGB color model and perform optimized rendering into the
556 Object rgbElem = null;
557 for (int y=startY; y<endY; y++)
559 int xoffset = offset;
560 for (int x=startX; x<endX; x++)
563 rgbElem = raster.getDataElements(x, y, rgbElem);
564 rgb = colorModel.getRGB(rgbElem);
565 rgbArray[xoffset++] = rgb;
567 offset += scanlineStride;
572 public WritableRaster getRaster()
577 public SampleModel getSampleModel()
579 return raster.getSampleModel();
582 public ImageProducer getSource()
584 return new ImageProducer()
586 Vector<ImageConsumer> consumers = new Vector<ImageConsumer>();
588 public void addConsumer(ImageConsumer ic)
590 if(!consumers.contains(ic))
594 public boolean isConsumer(ImageConsumer ic)
596 return consumers.contains(ic);
599 public void removeConsumer(ImageConsumer ic)
601 consumers.remove(ic);
604 public void startProduction(ImageConsumer ic)
608 int width = getWidth();
609 int height = getHeight();
612 int[] pixels = getRGB(x, y,
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,
622 for(int i = 0; i < consumers.size(); i++)
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);
632 public void requestTopDownLeftRightResend(ImageConsumer ic)
640 public Vector<RenderedImage> getSources()
645 public BufferedImage getSubimage(int x, int y, int w, int h)
647 WritableRaster subRaster =
648 getRaster().createWritableChild(x, y, w, h, 0, 0, null);
650 return new BufferedImage(getColorModel(), subRaster, isPremultiplied,
654 public Raster getTile(int tileX, int tileY)
656 return getWritableTile(tileX, tileY);
659 public int getTileGridXOffset()
661 return 0; // according to javadocs
664 public int getTileGridYOffset()
666 return 0; // according to javadocs
669 public int getTileHeight()
671 return getHeight(); // image is one big tile
674 public int getTileWidth()
676 return getWidth(); // image is one big tile
684 public int getWidth()
686 return raster.getWidth();
689 public int getWidth(ImageObserver imageobserver)
694 public WritableRaster getWritableTile(int tileX, int tileY)
696 isTileWritable(tileX, tileY); // for exception
700 private static final Point[] tileIndices = { new Point() };
702 public Point[] getWritableTileIndices()
707 public boolean hasTileWriters()
712 public boolean isAlphaPremultiplied()
714 return isPremultiplied;
717 public boolean isTileWritable(int tileX, int tileY)
719 if ((tileX != 0) || (tileY != 0))
720 throw new ArrayIndexOutOfBoundsException("only tile is (0,0)");
724 public void releaseWritableTile(int tileX, int tileY)
726 isTileWritable(tileX, tileY); // for exception
729 //public void removeTileObserver(TileObserver tileobserver) {}
731 public void setData(Raster src)
733 int x = src.getMinX();
734 int y = src.getMinY();
735 int w = src.getWidth();
736 int h = src.getHeight();
738 // create a dest child that has the right bounds...
739 WritableRaster dest =
740 raster.createWritableChild(x, y, w, h, x, y, null);
742 if (src.getSampleModel () instanceof ComponentSampleModel
743 && dest.getSampleModel () instanceof ComponentSampleModel)
745 // Refer to ComponentDataBlitOp for optimized data blitting:
746 ComponentDataBlitOp.INSTANCE.filter(src, dest);
750 int samples[] = src.getPixels (x, y, w, h, (int [])null);
751 dest.setPixels (x, y, w, h, samples);
755 public void setRGB(int x, int y, int argb)
757 Object rgbElem = colorModel.getDataElements(argb, null);
758 raster.setDataElements(x, y, rgbElem);
761 public void setRGB(int startX, int startY, int w, int h,
762 int[] argbArray, int offset, int scanlineStride)
764 int endX = startX + w;
765 int endY = startY + h;
767 Object rgbElem = null;
768 for (int y=startY; y<endY; y++)
770 int xoffset = offset;
771 for (int x=startX; x<endX; x++)
773 int argb = argbArray[xoffset++];
774 rgbElem = colorModel.getDataElements(argb, rgbElem);
775 raster.setDataElements(x, y, rgbElem);
777 offset += scanlineStride;
781 public String toString()
785 buf = new StringBuffer(/* estimated length */ 120);
786 buf.append("BufferedImage@");
787 buf.append(Integer.toHexString(hashCode()));
788 buf.append(": type=");
791 buf.append(colorModel);
795 return buf.toString();
800 * Adds a tile observer. If the observer is already present, it receives
801 * multiple notifications.
803 * @param to The TileObserver to add.
805 public void addTileObserver (TileObserver to)
807 if (tileObservers == null)
808 tileObservers = new Vector<TileObserver>();
810 tileObservers.add (to);
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.
818 * @param to The TileObserver to remove.
820 public void removeTileObserver (TileObserver to)
822 if (tileObservers == null)
825 tileObservers.remove (to);
829 * Return the transparency type.
831 * @return One of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT}.
832 * @see Transparency#getTransparency()
835 public int getTransparency()
837 return colorModel.getTransparency();