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