| /* Copyright (C) 2000 Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| package java.awt.image; |
| |
| import java.awt.*; |
| import java.awt.color.*; |
| import java.util.*; |
| |
| import gnu.gcj.awt.ComponentDataBlitOp; |
| |
| /** |
| * A buffered image always starts at coordinates (0, 0). |
| * |
| * The buffered image is not subdivided into multiple tiles. Instead, |
| * the image consists of one large tile (0,0) with the width and |
| * height of the image. This tile is always considered to be checked |
| * out. |
| * |
| * @author Rolf W. Rasmussen <rolfwr@ii.uib.no> |
| */ |
| public class BufferedImage extends java.awt.Image |
| //implements java.awt.image.WritableRenderedImage |
| { |
| public static final int TYPE_CUSTOM = 0, |
| TYPE_INT_RGB = 1, |
| TYPE_INT_ARGB = 2, |
| TYPE_INT_ARGB_PRE = 3, |
| TYPE_INT_BGR = 4, |
| TYPE_3BYTE_BGR = 5, |
| TYPE_4BYTE_ABGR = 6, |
| TYPE_4BYTE_ABGR_PRE = 7, |
| TYPE_USHORT_565_RGB = 8, |
| TYPE_USHORT_555_RGB = 9, |
| TYPE_BYTE_GRAY = 10, |
| TYPE_USHORT_GRAY = 11, |
| TYPE_BYTE_BINARY = 12, |
| TYPE_BYTE_INDEXED = 13; |
| |
| final static int[] bits3 = { 8, 8, 8 }; |
| final static int[] bits4 = { 8, 8, 8 }; |
| final static int[] bits1byte = { 8 }; |
| final static int[] bits1ushort = { 16 }; |
| |
| final static int[] masks_int = { 0x00ff0000, |
| 0x0000ff00, |
| 0x000000ff, |
| DataBuffer.TYPE_INT }; |
| final static int[] masks_565 = { 0xf800, |
| 0x07e0, |
| 0x001f, |
| DataBuffer.TYPE_USHORT}; |
| final static int[] masks_555 = { 0x7c00, |
| 0x03e0, |
| 0x001f, |
| DataBuffer.TYPE_USHORT}; |
| |
| public BufferedImage(int w, int h, int type) |
| { |
| ColorModel cm = null; |
| |
| boolean alpha = false; |
| boolean premultiplied = false; |
| switch (type) |
| { |
| case TYPE_4BYTE_ABGR_PRE: |
| case TYPE_INT_ARGB_PRE: |
| premultiplied = true; |
| // fall through |
| case TYPE_INT_ARGB: |
| case TYPE_4BYTE_ABGR: |
| alpha = true; |
| } |
| |
| ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); |
| switch (type) |
| { |
| case TYPE_INT_RGB: |
| case TYPE_INT_ARGB: |
| case TYPE_INT_ARGB_PRE: |
| case TYPE_USHORT_565_RGB: |
| case TYPE_USHORT_555_RGB: |
| int[] masks = null; |
| switch (type) |
| { |
| case TYPE_INT_RGB: |
| case TYPE_INT_ARGB: |
| case TYPE_INT_ARGB_PRE: |
| masks = masks_int; |
| break; |
| case TYPE_USHORT_565_RGB: |
| masks = masks_565; |
| break; |
| case TYPE_USHORT_555_RGB: |
| masks = masks_555; |
| break; |
| } |
| |
| cm = new DirectColorModel(cs, |
| 32, // 32 bits in an int |
| masks[0], // r |
| masks[1], // g |
| masks[2], // b |
| alpha ? 0xff000000 : 0, |
| premultiplied, |
| masks[3] // data type |
| ); |
| break; |
| |
| case TYPE_INT_BGR: |
| String msg = |
| "FIXME: Programmer is confused. Why (and how) does a " + |
| "TYPE_INT_BGR image use ComponentColorModel to store " + |
| "8-bit values? Is data type TYPE_INT or TYPE_BYTE. What " + |
| "is the difference between TYPE_INT_BGR and TYPE_3BYTE_BGR?"; |
| throw new UnsupportedOperationException(msg); |
| |
| case TYPE_3BYTE_BGR: |
| case TYPE_4BYTE_ABGR: |
| case TYPE_4BYTE_ABGR_PRE: |
| case TYPE_BYTE_GRAY: |
| case TYPE_USHORT_GRAY: |
| int[] bits = null; |
| int dataType = DataBuffer.TYPE_BYTE; |
| switch (type) { |
| case TYPE_3BYTE_BGR: |
| bits = bits3; |
| break; |
| case TYPE_4BYTE_ABGR: |
| case TYPE_4BYTE_ABGR_PRE: |
| bits = bits4; |
| break; |
| case TYPE_BYTE_GRAY: |
| bits = bits1byte; |
| break; |
| case TYPE_USHORT_GRAY: |
| bits = bits1ushort; |
| dataType = DataBuffer.TYPE_USHORT; |
| break; |
| } |
| cm = new ComponentColorModel(cs, bits, alpha, premultiplied, |
| alpha ? |
| Transparency.TRANSLUCENT: |
| Transparency.OPAQUE, |
| dataType); |
| break; |
| case TYPE_BYTE_BINARY: |
| byte[] vals = { 0, (byte) 0xff }; |
| cm = new IndexColorModel(8, 2, vals, vals, vals); |
| break; |
| case TYPE_BYTE_INDEXED: |
| String msg2 = "type not implemented yet"; |
| throw new UnsupportedOperationException(msg2); |
| // FIXME: build color-cube and create color model |
| } |
| |
| init(cm, |
| cm.createCompatibleWritableRaster(w, h), |
| premultiplied, |
| null, // no properties |
| type |
| ); |
| } |
| |
| public BufferedImage(int w, int h, int type, |
| IndexColorModel indexcolormodel) |
| { |
| if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED)) |
| throw new IllegalArgumentException("type must be binary or indexed"); |
| |
| init(indexcolormodel, |
| indexcolormodel.createCompatibleWritableRaster(w, h), |
| false, // not premultiplied (guess) |
| null, // no properties |
| type); |
| } |
| |
| public BufferedImage(ColorModel colormodel, |
| WritableRaster writableraster, |
| boolean premultiplied, |
| Hashtable properties) |
| { |
| init(colormodel, writableraster, premultiplied, properties, |
| TYPE_CUSTOM); |
| // TODO: perhaps try to identify type? |
| } |
| |
| WritableRaster raster; |
| ColorModel colorModel; |
| Hashtable properties; |
| boolean isPremultiplied; |
| int type; |
| |
| private void init(ColorModel cm, |
| WritableRaster writableraster, |
| boolean premultiplied, |
| Hashtable properties, |
| int type) |
| { |
| raster = writableraster; |
| colorModel = cm; |
| this.properties = properties; |
| isPremultiplied = premultiplied; |
| this.type = type; |
| } |
| |
| //public void addTileObserver(TileObserver tileobserver) {} |
| |
| public void coerceData(boolean premultiplied) |
| { |
| colorModel = colorModel.coerceData(raster, premultiplied); |
| } |
| |
| public WritableRaster copyData(WritableRaster dest) |
| { |
| if (dest == null) |
| dest = raster.createCompatibleWritableRaster(); |
| |
| int x = dest.getMinX(); |
| int y = dest.getMinY(); |
| int w = dest.getWidth(); |
| int h = dest.getHeight(); |
| |
| // create a src child that has the right bounds... |
| WritableRaster src = |
| raster.createWritableChild(x, y, w, h, x, y, |
| null // same bands |
| ); |
| |
| // Refer to ComponentDataBlitOp for optimized data blitting: |
| ComponentDataBlitOp.INSTANCE.filter(src, dest); |
| return dest; |
| } |
| |
| public Graphics2D createGraphics() |
| { |
| throw new UnsupportedOperationException("not implemented"); |
| // will require a lot of effort to implement |
| } |
| |
| public void flush() { |
| } |
| |
| public WritableRaster getAlphaRaster() |
| { |
| return colorModel.getAlphaRaster(raster); |
| } |
| |
| public ColorModel getColorModel() |
| { |
| return colorModel; |
| } |
| |
| public Raster getData() |
| { |
| return copyData(null); |
| /* TODO: this might be optimized by returning the same |
| raster (not writable) as long as image data doesn't change. */ |
| } |
| |
| public Raster getData(Rectangle rectangle) |
| { |
| WritableRaster dest = |
| raster.createCompatibleWritableRaster(rectangle); |
| return copyData(dest); |
| } |
| |
| public Graphics getGraphics() |
| { |
| return createGraphics(); |
| } |
| |
| public int getHeight() |
| { |
| return raster.getHeight(); |
| } |
| |
| public int getHeight(ImageObserver imageobserver) |
| { |
| return getHeight(); |
| } |
| |
| public int getMinTileX() |
| { |
| return 0; |
| } |
| |
| public int getMinTileY() |
| { |
| return 0; |
| } |
| |
| public int getMinX() |
| { |
| return 0; |
| } |
| |
| public int getMinY() |
| { |
| return 0; |
| } |
| |
| public int getNumXTiles() |
| { |
| return 1; |
| } |
| |
| public int getNumYTiles() |
| { |
| return 1; |
| } |
| |
| public Object getProperty(String string) |
| { |
| if (properties == null) |
| return null; |
| return properties.get(string); |
| } |
| |
| public Object getProperty(String string, ImageObserver imageobserver) |
| { |
| return getProperty(string); |
| } |
| |
| |
| public String[] getPropertyNames() |
| { |
| // FIXME: implement |
| return null; |
| } |
| |
| public int getRGB(int x, int y) |
| { |
| Object rgbElem = raster.getDataElements(x, y, |
| null // create as needed |
| ); |
| return colorModel.getRGB(rgbElem); |
| } |
| |
| public int[] getRGB(int startX, int startY, int w, int h, |
| int[] rgbArray, |
| int offset, int scanlineStride) |
| { |
| if (rgbArray == null) |
| { |
| /* |
| 000000000000000000 |
| 00000[#######----- [ = start |
| -----########----- ] = end |
| -----#######]00000 |
| 000000000000000000 */ |
| int size = (h-1)*scanlineStride + w; |
| rgbArray = new int[size]; |
| } |
| |
| int endX = startX + w; |
| int endY = startY + h; |
| |
| /* *TODO*: |
| Opportunity for optimization by examining color models... |
| |
| Perhaps wrap the rgbArray up in a WritableRaster with packed |
| sRGB color model and perform optimized rendering into the |
| array. */ |
| |
| Object rgbElem = null; |
| for (int y=startY; y<endY; y++) |
| { |
| int xoffset = offset; |
| for (int x=startX; x<endX; x++) |
| { |
| int rgb; |
| rgbElem = raster.getDataElements(x, y, rgbElem); |
| rgb = colorModel.getRGB(rgbElem); |
| rgbArray[xoffset++] = rgb; |
| } |
| offset += scanlineStride; |
| } |
| return rgbArray; |
| } |
| |
| public WritableRaster getRaster() |
| { |
| return raster; |
| } |
| |
| public SampleModel getSampleModel() |
| { |
| return raster.getSampleModel(); |
| } |
| |
| public ImageProducer getSource() |
| { |
| throw new UnsupportedOperationException("not implemented"); |
| } |
| |
| public Vector getSources() |
| { |
| return null; |
| } |
| |
| public BufferedImage getSubimage(int x, int y, int w, int h) |
| { |
| WritableRaster subRaster = |
| getRaster().createWritableChild(x, y, w, h, 0, 0, null); |
| |
| return new BufferedImage(getColorModel(), |
| subRaster, |
| isPremultiplied, |
| properties); |
| } |
| |
| public Raster getTile(int tileX, int tileY) |
| { |
| return getWritableTile(tileX, tileY); |
| } |
| |
| public int getTileGridXOffset() |
| { |
| return 0; // according to javadocs |
| } |
| |
| public int getTileGridYOffset() |
| { |
| return 0; // according to javadocs |
| } |
| |
| public int getTileHeight() |
| { |
| return getHeight(); // image is one big tile |
| } |
| |
| public int getTileWidth() |
| { |
| return getWidth(); // image is one big tile |
| } |
| |
| public int getType() |
| { |
| return type; |
| } |
| |
| public int getWidth() |
| { |
| return raster.getWidth(); |
| } |
| |
| public int getWidth(ImageObserver imageobserver) |
| { |
| return getWidth(); |
| } |
| |
| public WritableRaster getWritableTile(int tileX, int tileY) |
| { |
| isTileWritable(tileX, tileY); // for exception |
| return raster; |
| } |
| |
| private static final Point[] tileIndices = { new Point() }; |
| |
| public Point[] getWritableTileIndices() |
| { |
| return tileIndices; |
| } |
| |
| public boolean hasTileWriters() |
| { |
| return true; |
| } |
| |
| public boolean isAlphaPremultiplied() |
| { |
| return isPremultiplied; |
| } |
| |
| public boolean isTileWritable(int tileX, int tileY) |
| { |
| if ((tileX != 0) || (tileY != 0)) |
| throw new ArrayIndexOutOfBoundsException("only tile is (0,0)"); |
| return true; |
| } |
| |
| public void releaseWritableTile(int tileX, int tileY) |
| { |
| isTileWritable(tileX, tileY); // for exception |
| } |
| |
| //public void removeTileObserver(TileObserver tileobserver) {} |
| |
| public void setData(Raster src) |
| { |
| int x = src.getMinX(); |
| int y = src.getMinY(); |
| int w = src.getWidth(); |
| int h = src.getHeight(); |
| |
| // create a dest child that has the right bounds... |
| WritableRaster dest = |
| raster.createWritableChild(x, y, w, h, x, y, |
| null // same bands |
| ); |
| |
| // Refer to ComponentDataBlitOp for optimized data blitting: |
| ComponentDataBlitOp.INSTANCE.filter(src, dest); |
| } |
| |
| public void setRGB(int x, int y, int argb) |
| { |
| Object rgbElem = colorModel.getDataElements(argb, null); |
| raster.setDataElements(x, y, rgbElem); |
| } |
| |
| public void setRGB(int startX, int startY, int w, int h, |
| int[] argbArray, int offset, int scanlineStride) |
| { |
| int endX = startX + w; |
| int endY = startY + h; |
| |
| Object rgbElem = null; |
| for (int y=startY; y<endY; y++) |
| { |
| int xoffset = offset; |
| for (int x=startX; x<endX; x++) |
| { |
| int argb = argbArray[xoffset++]; |
| rgbElem = colorModel.getDataElements(argb, rgbElem); |
| raster.setDataElements(x, y, rgbElem); |
| } |
| offset += scanlineStride; |
| } |
| } |
| |
| public String toString() |
| { |
| // FIXME: implement: |
| return super.toString(); |
| } |
| } |