/* |
|
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package java.awt.image; |
|
import java.awt.Graphics2D; |
|
import java.awt.GraphicsEnvironment; |
|
import java.awt.Point; |
|
import java.awt.Rectangle; |
|
import java.awt.Transparency; |
|
import java.awt.color.ColorSpace; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.Hashtable; |
|
import java.util.Set; |
|
import java.util.Vector; |
|
import sun.awt.image.ByteComponentRaster; |
|
import sun.awt.image.BytePackedRaster; |
|
import sun.awt.image.IntegerComponentRaster; |
|
import sun.awt.image.OffScreenImageSource; |
|
import sun.awt.image.ShortComponentRaster; |
|
/** |
|
* |
|
* The <code>BufferedImage</code> subclass describes an {@link |
|
* java.awt.Image Image} with an accessible buffer of image data. |
|
* A <code>BufferedImage</code> is comprised of a {@link ColorModel} and a |
|
* {@link Raster} of image data. |
|
* The number and types of bands in the {@link SampleModel} of the |
|
* <code>Raster</code> must match the number and types required by the |
|
* <code>ColorModel</code> to represent its color and alpha components. |
|
* All <code>BufferedImage</code> objects have an upper left corner |
|
* coordinate of (0, 0). Any <code>Raster</code> used to construct a |
|
* <code>BufferedImage</code> must therefore have minX=0 and minY=0. |
|
* |
|
* <p> |
|
* This class relies on the data fetching and setting methods |
|
* of <code>Raster</code>, |
|
* and on the color characterization methods of <code>ColorModel</code>. |
|
* |
|
* @see ColorModel |
|
* @see Raster |
|
* @see WritableRaster |
|
*/ |
|
public class BufferedImage extends java.awt.Image |
|
implements WritableRenderedImage, Transparency |
|
{ |
|
private int imageType = TYPE_CUSTOM; |
|
private ColorModel colorModel; |
|
private final WritableRaster raster; |
|
private OffScreenImageSource osis; |
|
private Hashtable<String, Object> properties; |
|
/** |
|
* Image Type Constants |
|
*/ |
|
/** |
|
* Image type is not recognized so it must be a customized |
|
* image. This type is only used as a return value for the getType() |
|
* method. |
|
*/ |
|
public static final int TYPE_CUSTOM = 0; |
|
/** |
|
* Represents an image with 8-bit RGB color components packed into |
|
* integer pixels. The image has a {@link DirectColorModel} without |
|
* alpha. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_INT_RGB = 1; |
|
/** |
|
* Represents an image with 8-bit RGBA color components packed into |
|
* integer pixels. The image has a <code>DirectColorModel</code> |
|
* with alpha. The color data in this image is considered not to be |
|
* premultiplied with alpha. When this type is used as the |
|
* <code>imageType</code> argument to a <code>BufferedImage</code> |
|
* constructor, the created image is consistent with images |
|
* created in the JDK1.1 and earlier releases. |
|
*/ |
|
public static final int TYPE_INT_ARGB = 2; |
|
/** |
|
* Represents an image with 8-bit RGBA color components packed into |
|
* integer pixels. The image has a <code>DirectColorModel</code> |
|
* with alpha. The color data in this image is considered to be |
|
* premultiplied with alpha. |
|
*/ |
|
public static final int TYPE_INT_ARGB_PRE = 3; |
|
/** |
|
* Represents an image with 8-bit RGB color components, corresponding |
|
* to a Windows- or Solaris- style BGR color model, with the colors |
|
* Blue, Green, and Red packed into integer pixels. There is no alpha. |
|
* The image has a {@link DirectColorModel}. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_INT_BGR = 4; |
|
/** |
|
* Represents an image with 8-bit RGB color components, corresponding |
|
* to a Windows-style BGR color model) with the colors Blue, Green, |
|
* and Red stored in 3 bytes. There is no alpha. The image has a |
|
* <code>ComponentColorModel</code>. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_3BYTE_BGR = 5; |
|
/** |
|
* Represents an image with 8-bit RGBA color components with the colors |
|
* Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The |
|
* image has a <code>ComponentColorModel</code> with alpha. The |
|
* color data in this image is considered not to be premultiplied with |
|
* alpha. The byte data is interleaved in a single |
|
* byte array in the order A, B, G, R |
|
* from lower to higher byte addresses within each pixel. |
|
*/ |
|
public static final int TYPE_4BYTE_ABGR = 6; |
|
/** |
|
* Represents an image with 8-bit RGBA color components with the colors |
|
* Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The |
|
* image has a <code>ComponentColorModel</code> with alpha. The color |
|
* data in this image is considered to be premultiplied with alpha. |
|
* The byte data is interleaved in a single byte array in the order |
|
* A, B, G, R from lower to higher byte addresses within each pixel. |
|
*/ |
|
public static final int TYPE_4BYTE_ABGR_PRE = 7; |
|
/** |
|
* Represents an image with 5-6-5 RGB color components (5-bits red, |
|
* 6-bits green, 5-bits blue) with no alpha. This image has |
|
* a <code>DirectColorModel</code>. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_USHORT_565_RGB = 8; |
|
/** |
|
* Represents an image with 5-5-5 RGB color components (5-bits red, |
|
* 5-bits green, 5-bits blue) with no alpha. This image has |
|
* a <code>DirectColorModel</code>. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_USHORT_555_RGB = 9; |
|
/** |
|
* Represents a unsigned byte grayscale image, non-indexed. This |
|
* image has a <code>ComponentColorModel</code> with a CS_GRAY |
|
* {@link ColorSpace}. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_BYTE_GRAY = 10; |
|
/** |
|
* Represents an unsigned short grayscale image, non-indexed). This |
|
* image has a <code>ComponentColorModel</code> with a CS_GRAY |
|
* <code>ColorSpace</code>. |
|
* When data with non-opaque alpha is stored |
|
* in an image of this type, |
|
* the color data must be adjusted to a non-premultiplied form |
|
* and the alpha discarded, |
|
* as described in the |
|
* {@link java.awt.AlphaComposite} documentation. |
|
*/ |
|
public static final int TYPE_USHORT_GRAY = 11; |
|
/** |
|
* Represents an opaque byte-packed 1, 2, or 4 bit image. The |
|
* image has an {@link IndexColorModel} without alpha. When this |
|
* type is used as the <code>imageType</code> argument to the |
|
* <code>BufferedImage</code> constructor that takes an |
|
* <code>imageType</code> argument but no <code>ColorModel</code> |
|
* argument, a 1-bit image is created with an |
|
* <code>IndexColorModel</code> with two colors in the default |
|
* sRGB <code>ColorSpace</code>: {0, 0, 0} and |
|
* {255, 255, 255}. |
|
* |
|
* <p> Images with 2 or 4 bits per pixel may be constructed via |
|
* the <code>BufferedImage</code> constructor that takes a |
|
* <code>ColorModel</code> argument by supplying a |
|
* <code>ColorModel</code> with an appropriate map size. |
|
* |
|
* <p> Images with 8 bits per pixel should use the image types |
|
* <code>TYPE_BYTE_INDEXED</code> or <code>TYPE_BYTE_GRAY</code> |
|
* depending on their <code>ColorModel</code>. |
|
|
|
* <p> When color data is stored in an image of this type, |
|
* the closest color in the colormap is determined |
|
* by the <code>IndexColorModel</code> and the resulting index is stored. |
|
* Approximation and loss of alpha or color components |
|
* can result, depending on the colors in the |
|
* <code>IndexColorModel</code> colormap. |
|
*/ |
|
public static final int TYPE_BYTE_BINARY = 12; |
|
/** |
|
* Represents an indexed byte image. When this type is used as the |
|
* <code>imageType</code> argument to the <code>BufferedImage</code> |
|
* constructor that takes an <code>imageType</code> argument |
|
* but no <code>ColorModel</code> argument, an |
|
* <code>IndexColorModel</code> is created with |
|
* a 256-color 6/6/6 color cube palette with the rest of the colors |
|
* from 216-255 populated by grayscale values in the |
|
* default sRGB ColorSpace. |
|
* |
|
* <p> When color data is stored in an image of this type, |
|
* the closest color in the colormap is determined |
|
* by the <code>IndexColorModel</code> and the resulting index is stored. |
|
* Approximation and loss of alpha or color components |
|
* can result, depending on the colors in the |
|
* <code>IndexColorModel</code> colormap. |
|
*/ |
|
public static final int TYPE_BYTE_INDEXED = 13; |
|
private static final int DCM_RED_MASK = 0x00ff0000; |
|
private static final int DCM_GREEN_MASK = 0x0000ff00; |
|
private static final int DCM_BLUE_MASK = 0x000000ff; |
|
private static final int DCM_ALPHA_MASK = 0xff000000; |
|
private static final int DCM_565_RED_MASK = 0xf800; |
|
private static final int DCM_565_GRN_MASK = 0x07E0; |
|
private static final int DCM_565_BLU_MASK = 0x001F; |
|
private static final int DCM_555_RED_MASK = 0x7C00; |
|
private static final int DCM_555_GRN_MASK = 0x03E0; |
|
private static final int DCM_555_BLU_MASK = 0x001F; |
|
private static final int DCM_BGR_RED_MASK = 0x0000ff; |
|
private static final int DCM_BGR_GRN_MASK = 0x00ff00; |
|
private static final int DCM_BGR_BLU_MASK = 0xff0000; |
|
static private native void initIDs(); |
|
static { |
|
ColorModel.loadLibraries(); |
|
initIDs(); |
|
} |
|
/** |
|
* Constructs a <code>BufferedImage</code> of one of the predefined |
|
* image types. The <code>ColorSpace</code> for the image is the |
|
* default sRGB space. |
|
* @param width width of the created image |
|
* @param height height of the created image |
|
* @param imageType type of the created image |
|
* @see ColorSpace |
|
* @see #TYPE_INT_RGB |
|
* @see #TYPE_INT_ARGB |
|
* @see #TYPE_INT_ARGB_PRE |
|
* @see #TYPE_INT_BGR |
|
* @see #TYPE_3BYTE_BGR |
|
* @see #TYPE_4BYTE_ABGR |
|
* @see #TYPE_4BYTE_ABGR_PRE |
|
* @see #TYPE_BYTE_GRAY |
|
* @see #TYPE_USHORT_GRAY |
|
* @see #TYPE_BYTE_BINARY |
|
* @see #TYPE_BYTE_INDEXED |
|
* @see #TYPE_USHORT_565_RGB |
|
* @see #TYPE_USHORT_555_RGB |
|
*/ |
|
public BufferedImage(int width, |
|
int height, |
|
int imageType) { |
|
switch (imageType) { |
|
case TYPE_INT_RGB: |
|
{ |
|
colorModel = new DirectColorModel(24, |
|
0x00ff0000, // Red |
|
0x0000ff00, // Green |
|
0x000000ff, // Blue |
|
0x0 // Alpha |
|
); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_INT_ARGB: |
|
{ |
|
colorModel = ColorModel.getRGBdefault(); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_INT_ARGB_PRE: |
|
{ |
|
colorModel = new |
|
DirectColorModel( |
|
ColorSpace.getInstance(ColorSpace.CS_sRGB), |
|
32, |
|
0x00ff0000,// Red |
|
0x0000ff00,// Green |
|
0x000000ff,// Blue |
|
0xff000000,// Alpha |
|
true, // Alpha Premultiplied |
|
DataBuffer.TYPE_INT |
|
); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_INT_BGR: |
|
{ |
|
colorModel = new DirectColorModel(24, |
|
0x000000ff, // Red |
|
0x0000ff00, // Green |
|
0x00ff0000 // Blue |
|
); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_3BYTE_BGR: |
|
{ |
|
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); |
|
int[] nBits = {8, 8, 8}; |
|
int[] bOffs = {2, 1, 0}; |
|
colorModel = new ComponentColorModel(cs, nBits, false, false, |
|
Transparency.OPAQUE, |
|
DataBuffer.TYPE_BYTE); |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, |
|
width*3, 3, |
|
bOffs, null); |
|
} |
|
break; |
|
case TYPE_4BYTE_ABGR: |
|
{ |
|
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); |
|
int[] nBits = {8, 8, 8, 8}; |
|
int[] bOffs = {3, 2, 1, 0}; |
|
colorModel = new ComponentColorModel(cs, nBits, true, false, |
|
Transparency.TRANSLUCENT, |
|
DataBuffer.TYPE_BYTE); |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, |
|
width*4, 4, |
|
bOffs, null); |
|
} |
|
break; |
|
case TYPE_4BYTE_ABGR_PRE: |
|
{ |
|
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); |
|
int[] nBits = {8, 8, 8, 8}; |
|
int[] bOffs = {3, 2, 1, 0}; |
|
colorModel = new ComponentColorModel(cs, nBits, true, true, |
|
Transparency.TRANSLUCENT, |
|
DataBuffer.TYPE_BYTE); |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, |
|
width*4, 4, |
|
bOffs, null); |
|
} |
|
break; |
|
case TYPE_BYTE_GRAY: |
|
{ |
|
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); |
|
int[] nBits = {8}; |
|
colorModel = new ComponentColorModel(cs, nBits, false, true, |
|
Transparency.OPAQUE, |
|
DataBuffer.TYPE_BYTE); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_USHORT_GRAY: |
|
{ |
|
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); |
|
int[] nBits = {16}; |
|
colorModel = new ComponentColorModel(cs, nBits, false, true, |
|
Transparency.OPAQUE, |
|
DataBuffer.TYPE_USHORT); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_BYTE_BINARY: |
|
{ |
|
byte[] arr = {(byte)0, (byte)0xff}; |
|
colorModel = new IndexColorModel(1, 2, arr, arr, arr); |
|
raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, 1, 1, null); |
|
} |
|
break; |
|
case TYPE_BYTE_INDEXED: |
|
{ |
|
// Create a 6x6x6 color cube |
|
int[] cmap = new int[256]; |
|
int i=0; |
|
for (int r=0; r < 256; r += 51) { |
|
for (int g=0; g < 256; g += 51) { |
|
for (int b=0; b < 256; b += 51) { |
|
cmap[i++] = (r<<16)|(g<<8)|b; |
|
} |
|
} |
|
} |
|
// And populate the rest of the cmap with gray values |
|
int grayIncr = 256/(256-i); |
|
// The gray ramp will be between 18 and 252 |
|
int gray = grayIncr*3; |
|
for (; i < 256; i++) { |
|
cmap[i] = (gray<<16)|(gray<<8)|gray; |
|
gray += grayIncr; |
|
} |
|
colorModel = new IndexColorModel(8, 256, cmap, 0, false, -1, |
|
DataBuffer.TYPE_BYTE); |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, 1, null); |
|
} |
|
break; |
|
case TYPE_USHORT_565_RGB: |
|
{ |
|
colorModel = new DirectColorModel(16, |
|
DCM_565_RED_MASK, |
|
DCM_565_GRN_MASK, |
|
DCM_565_BLU_MASK |
|
); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
case TYPE_USHORT_555_RGB: |
|
{ |
|
colorModel = new DirectColorModel(15, |
|
DCM_555_RED_MASK, |
|
DCM_555_GRN_MASK, |
|
DCM_555_BLU_MASK |
|
); |
|
raster = colorModel.createCompatibleWritableRaster(width, |
|
height); |
|
} |
|
break; |
|
default: |
|
throw new IllegalArgumentException ("Unknown image type " + |
|
imageType); |
|
} |
|
this.imageType = imageType; |
|
} |
|
/** |
|
* Constructs a <code>BufferedImage</code> of one of the predefined |
|
* image types: |
|
* TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED. |
|
* |
|
* <p> If the image type is TYPE_BYTE_BINARY, the number of |
|
* entries in the color model is used to determine whether the |
|
* image should have 1, 2, or 4 bits per pixel. If the color model |
|
* has 1 or 2 entries, the image will have 1 bit per pixel. If it |
|
* has 3 or 4 entries, the image with have 2 bits per pixel. If |
|
* it has between 5 and 16 entries, the image will have 4 bits per |
|
* pixel. Otherwise, an IllegalArgumentException will be thrown. |
|
* |
|
* @param width width of the created image |
|
* @param height height of the created image |
|
* @param imageType type of the created image |
|
* @param cm <code>IndexColorModel</code> of the created image |
|
* @throws IllegalArgumentException if the imageType is not |
|
* TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED or if the imageType is |
|
* TYPE_BYTE_BINARY and the color map has more than 16 entries. |
|
* @see #TYPE_BYTE_BINARY |
|
* @see #TYPE_BYTE_INDEXED |
|
*/ |
|
public BufferedImage (int width, |
|
int height, |
|
int imageType, |
|
IndexColorModel cm) { |
|
if (cm.hasAlpha() && cm.isAlphaPremultiplied()) { |
|
throw new IllegalArgumentException("This image types do not have "+ |
|
"premultiplied alpha."); |
|
} |
|
switch(imageType) { |
|
case TYPE_BYTE_BINARY: |
|
int bits; // Will be set below |
|
int mapSize = cm.getMapSize(); |
|
if (mapSize <= 2) { |
|
bits = 1; |
|
} else if (mapSize <= 4) { |
|
bits = 2; |
|
} else if (mapSize <= 16) { |
|
bits = 4; |
|
} else { |
|
throw new IllegalArgumentException |
|
("Color map for TYPE_BYTE_BINARY " + |
|
"must have no more than 16 entries"); |
|
} |
|
raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, 1, bits, null); |
|
break; |
|
case TYPE_BYTE_INDEXED: |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
width, height, 1, null); |
|
break; |
|
default: |
|
throw new IllegalArgumentException("Invalid image type (" + |
|
imageType+"). Image type must"+ |
|
" be either TYPE_BYTE_BINARY or "+ |
|
" TYPE_BYTE_INDEXED"); |
|
} |
|
if (!cm.isCompatibleRaster(raster)) { |
|
throw new IllegalArgumentException("Incompatible image type and IndexColorModel"); |
|
} |
|
colorModel = cm; |
|
this.imageType = imageType; |
|
} |
|
/** |
|
* Constructs a new <code>BufferedImage</code> with a specified |
|
* <code>ColorModel</code> and <code>Raster</code>. If the number and |
|
* types of bands in the <code>SampleModel</code> of the |
|
* <code>Raster</code> do not match the number and types required by |
|
* the <code>ColorModel</code> to represent its color and alpha |
|
* components, a {@link RasterFormatException} is thrown. This |
|
* method can multiply or divide the color <code>Raster</code> data by |
|
* alpha to match the <code>alphaPremultiplied</code> state |
|
* in the <code>ColorModel</code>. Properties for this |
|
* <code>BufferedImage</code> can be established by passing |
|
* in a {@link Hashtable} of <code>String</code>/<code>Object</code> |
|
* pairs. |
|
* @param cm <code>ColorModel</code> for the new image |
|
* @param raster <code>Raster</code> for the image data |
|
* @param isRasterPremultiplied if <code>true</code>, the data in |
|
* the raster has been premultiplied with alpha. |
|
* @param properties <code>Hashtable</code> of |
|
* <code>String</code>/<code>Object</code> pairs. |
|
* @exception RasterFormatException if the number and |
|
* types of bands in the <code>SampleModel</code> of the |
|
* <code>Raster</code> do not match the number and types required by |
|
* the <code>ColorModel</code> to represent its color and alpha |
|
* components. |
|
* @exception IllegalArgumentException if |
|
* <code>raster</code> is incompatible with <code>cm</code> |
|
* @see ColorModel |
|
* @see Raster |
|
* @see WritableRaster |
|
*/ |
|
/* |
|
* |
|
* FOR NOW THE CODE WHICH DEFINES THE RASTER TYPE IS DUPLICATED BY DVF |
|
* SEE THE METHOD DEFINERASTERTYPE @ RASTEROUTPUTMANAGER |
|
* |
|
*/ |
|
public BufferedImage (ColorModel cm, |
|
WritableRaster raster, |
|
boolean isRasterPremultiplied, |
|
Hashtable<?,?> properties) { |
|
if (!cm.isCompatibleRaster(raster)) { |
|
throw new |
|
IllegalArgumentException("Raster "+raster+ |
|
" is incompatible with ColorModel "+ |
|
cm); |
|
} |
|
if ((raster.minX != 0) || (raster.minY != 0)) { |
|
throw new |
|
IllegalArgumentException("Raster "+raster+ |
|
" has minX or minY not equal to zero: " |
|
+ raster.minX + " " + raster.minY); |
|
} |
|
colorModel = cm; |
|
this.raster = raster; |
|
if (properties != null && !properties.isEmpty()) { |
|
this.properties = new Hashtable<>(); |
|
for (final Object key : properties.keySet()) { |
|
if (key instanceof String) { |
|
this.properties.put((String) key, properties.get(key)); |
|
} |
|
} |
|
} |
|
int numBands = raster.getNumBands(); |
|
boolean isAlphaPre = cm.isAlphaPremultiplied(); |
|
final boolean isStandard = isStandard(cm, raster); |
|
ColorSpace cs; |
|
// Force the raster data alpha state to match the premultiplied |
|
// state in the color model |
|
coerceData(isRasterPremultiplied); |
|
SampleModel sm = raster.getSampleModel(); |
|
cs = cm.getColorSpace(); |
|
int csType = cs.getType(); |
|
if (csType != ColorSpace.TYPE_RGB) { |
|
if (csType == ColorSpace.TYPE_GRAY && |
|
isStandard && |
|
cm instanceof ComponentColorModel) { |
|
// Check if this might be a child raster (fix for bug 4240596) |
|
if (sm instanceof ComponentSampleModel && |
|
((ComponentSampleModel)sm).getPixelStride() != numBands) { |
|
imageType = TYPE_CUSTOM; |
|
} else if (raster instanceof ByteComponentRaster && |
|
raster.getNumBands() == 1 && |
|
cm.getComponentSize(0) == 8 && |
|
((ByteComponentRaster)raster).getPixelStride() == 1) { |
|
imageType = TYPE_BYTE_GRAY; |
|
} else if (raster instanceof ShortComponentRaster && |
|
raster.getNumBands() == 1 && |
|
cm.getComponentSize(0) == 16 && |
|
((ShortComponentRaster)raster).getPixelStride() == 1) { |
|
imageType = TYPE_USHORT_GRAY; |
|
} |
|
} else { |
|
imageType = TYPE_CUSTOM; |
|
} |
|
return; |
|
} |
|
if ((raster instanceof IntegerComponentRaster) && |
|
(numBands == 3 || numBands == 4)) { |
|
IntegerComponentRaster iraster = |
|
(IntegerComponentRaster) raster; |
|
// Check if the raster params and the color model |
|
// are correct |
|
int pixSize = cm.getPixelSize(); |
|
if (iraster.getPixelStride() == 1 && |
|
isStandard && |
|
cm instanceof DirectColorModel && |
|
(pixSize == 32 || pixSize == 24)) |
|
{ |
|
// Now check on the DirectColorModel params |
|
DirectColorModel dcm = (DirectColorModel) cm; |
|
int rmask = dcm.getRedMask(); |
|
int gmask = dcm.getGreenMask(); |
|
int bmask = dcm.getBlueMask(); |
|
if (rmask == DCM_RED_MASK && gmask == DCM_GREEN_MASK && |
|
bmask == DCM_BLUE_MASK) |
|
{ |
|
if (dcm.getAlphaMask() == DCM_ALPHA_MASK) { |
|
imageType = (isAlphaPre |
|
? TYPE_INT_ARGB_PRE |
|
: TYPE_INT_ARGB); |
|
} |
|
else { |
|
// No Alpha |
|
if (!dcm.hasAlpha()) { |
|
imageType = TYPE_INT_RGB; |
|
} |
|
} |
|
} // if (dcm.getRedMask() == DCM_RED_MASK && |
|
else if (rmask == DCM_BGR_RED_MASK && gmask == DCM_BGR_GRN_MASK |
|
&& bmask == DCM_BGR_BLU_MASK) { |
|
if (!dcm.hasAlpha()) { |
|
imageType = TYPE_INT_BGR; |
|
} |
|
} // if (rmask == DCM_BGR_RED_MASK && |
|
} // if (iraster.getPixelStride() == 1 |
|
} // ((raster instanceof IntegerComponentRaster) && |
|
else if ((cm instanceof IndexColorModel) && (numBands == 1) && |
|
isStandard && |
|
(!cm.hasAlpha() || !isAlphaPre)) |
|
{ |
|
IndexColorModel icm = (IndexColorModel) cm; |
|
int pixSize = icm.getPixelSize(); |
|
if (raster instanceof BytePackedRaster) { |
|
imageType = TYPE_BYTE_BINARY; |
|
} // if (raster instanceof BytePackedRaster) |
|
else if (raster instanceof ByteComponentRaster) { |
|
ByteComponentRaster braster = (ByteComponentRaster) raster; |
|
if (braster.getPixelStride() == 1 && pixSize <= 8) { |
|
imageType = TYPE_BYTE_INDEXED; |
|
} |
|
} |
|
} // else if (cm instanceof IndexColorModel) && (numBands == 1)) |
|
else if ((raster instanceof ShortComponentRaster) |
|
&& (cm instanceof DirectColorModel) |
|
&& isStandard |
|
&& (numBands == 3) |
|
&& !cm.hasAlpha()) |
|
{ |
|
DirectColorModel dcm = (DirectColorModel) cm; |
|
if (dcm.getRedMask() == DCM_565_RED_MASK) { |
|
if (dcm.getGreenMask() == DCM_565_GRN_MASK && |
|
dcm.getBlueMask() == DCM_565_BLU_MASK) { |
|
imageType = TYPE_USHORT_565_RGB; |
|
} |
|
} |
|
else if (dcm.getRedMask() == DCM_555_RED_MASK) { |
|
if (dcm.getGreenMask() == DCM_555_GRN_MASK && |
|
dcm.getBlueMask() == DCM_555_BLU_MASK) { |
|
imageType = TYPE_USHORT_555_RGB; |
|
} |
|
} |
|
} // else if ((cm instanceof IndexColorModel) && (numBands == 1)) |
|
else if ((raster instanceof ByteComponentRaster) |
|
&& (cm instanceof ComponentColorModel) |
|
&& isStandard |
|
&& (raster.getSampleModel() instanceof PixelInterleavedSampleModel) |
|
&& (numBands == 3 || numBands == 4)) |
|
{ |
|
ComponentColorModel ccm = (ComponentColorModel) cm; |
|
PixelInterleavedSampleModel csm = |
|
(PixelInterleavedSampleModel)raster.getSampleModel(); |
|
ByteComponentRaster braster = (ByteComponentRaster) raster; |
|
int[] offs = csm.getBandOffsets(); |
|
if (ccm.getNumComponents() != numBands) { |
|
throw new RasterFormatException("Number of components in "+ |
|
"ColorModel ("+ |
|
ccm.getNumComponents()+ |
|
") does not match # in "+ |
|
" Raster ("+numBands+")"); |
|
} |
|
int[] nBits = ccm.getComponentSize(); |
|
boolean is8bit = true; |
|
for (int i=0; i < numBands; i++) { |
|
if (nBits[i] != 8) { |
|
is8bit = false; |
|
break; |
|
} |
|
} |
|
if (is8bit && |
|
braster.getPixelStride() == numBands && |
|
offs[0] == numBands-1 && |
|
offs[1] == numBands-2 && |
|
offs[2] == numBands-3) |
|
{ |
|
if (numBands == 3 && !ccm.hasAlpha()) { |
|
imageType = TYPE_3BYTE_BGR; |
|
} |
|
else if (offs[3] == 0 && ccm.hasAlpha()) { |
|
imageType = (isAlphaPre |
|
? TYPE_4BYTE_ABGR_PRE |
|
: TYPE_4BYTE_ABGR); |
|
} |
|
} |
|
} // else if ((raster instanceof ByteComponentRaster) && |
|
} |
|
private static boolean isStandard(ColorModel cm, WritableRaster wr) { |
|
final Class<? extends ColorModel> cmClass = cm.getClass(); |
|
final Class<? extends WritableRaster> wrClass = wr.getClass(); |
|
final Class<? extends SampleModel> smClass = wr.getSampleModel().getClass(); |
|
final PrivilegedAction<Boolean> checkClassLoadersAction = |
|
new PrivilegedAction<Boolean>() |
|
{ |
|
@Override |
|
public Boolean run() { |
|
final ClassLoader std = System.class.getClassLoader(); |
|
return (cmClass.getClassLoader() == std) && |
|
(smClass.getClassLoader() == std) && |
|
(wrClass.getClassLoader() == std); |
|
} |
|
}; |
|
return AccessController.doPrivileged(checkClassLoadersAction); |
|
} |
|
/** |
|
* Returns the image type. If it is not one of the known types, |
|
* TYPE_CUSTOM is returned. |
|
* @return the image type of this <code>BufferedImage</code>. |
|
* @see #TYPE_INT_RGB |
|
* @see #TYPE_INT_ARGB |
|
* @see #TYPE_INT_ARGB_PRE |
|
* @see #TYPE_INT_BGR |
|
* @see #TYPE_3BYTE_BGR |
|
* @see #TYPE_4BYTE_ABGR |
|
* @see #TYPE_4BYTE_ABGR_PRE |
|
* @see #TYPE_BYTE_GRAY |
|
* @see #TYPE_BYTE_BINARY |
|
* @see #TYPE_BYTE_INDEXED |
|
* @see #TYPE_USHORT_GRAY |
|
* @see #TYPE_USHORT_565_RGB |
|
* @see #TYPE_USHORT_555_RGB |
|
* @see #TYPE_CUSTOM |
|
*/ |
|
public int getType() { |
|
return imageType; |
|
} |
|
/** |
|
* Returns the <code>ColorModel</code>. |
|
* @return the <code>ColorModel</code> of this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public ColorModel getColorModel() { |
|
return colorModel; |
|
} |
|
/** |
|
* Returns the {@link WritableRaster}. |
|
* @return the <code>WriteableRaster</code> of this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public WritableRaster getRaster() { |
|
return raster; |
|
} |
|
/** |
|
* Returns a <code>WritableRaster</code> representing the alpha |
|
* channel for <code>BufferedImage</code> objects |
|
* with <code>ColorModel</code> objects that support a separate |
|
* spatial alpha channel, such as <code>ComponentColorModel</code> and |
|
* <code>DirectColorModel</code>. Returns <code>null</code> if there |
|
* is no alpha channel associated with the <code>ColorModel</code> in |
|
* this image. This method assumes that for all |
|
* <code>ColorModel</code> objects other than |
|
* <code>IndexColorModel</code>, if the <code>ColorModel</code> |
|
* supports alpha, there is a separate alpha channel |
|
* which is stored as the last band of image data. |
|
* If the image uses an <code>IndexColorModel</code> that |
|
* has alpha in the lookup table, this method returns |
|
* <code>null</code> since there is no spatially discrete alpha |
|
* channel. This method creates a new |
|
* <code>WritableRaster</code>, but shares the data array. |
|
* @return a <code>WritableRaster</code> or <code>null</code> if this |
|
* <code>BufferedImage</code> has no alpha channel associated |
|
* with its <code>ColorModel</code>. |
|
*/ |
|
public WritableRaster getAlphaRaster() { |
|
return colorModel.getAlphaRaster(raster); |
|
} |
|
/** |
|
* Returns an integer pixel in the default RGB color model |
|
* (TYPE_INT_ARGB) and default sRGB colorspace. Color |
|
* conversion takes place if this default model does not match |
|
* the image <code>ColorModel</code>. There are only 8-bits of |
|
* precision for each color component in the returned data when using |
|
* this method. |
|
* |
|
* <p> |
|
* |
|
* An <code>ArrayOutOfBoundsException</code> may be thrown |
|
* if the coordinates are not in bounds. |
|
* However, explicit bounds checking is not guaranteed. |
|
* |
|
* @param x the X coordinate of the pixel from which to get |
|
* the pixel in the default RGB color model and sRGB |
|
* color space |
|
* @param y the Y coordinate of the pixel from which to get |
|
* the pixel in the default RGB color model and sRGB |
|
* color space |
|
* @return an integer pixel in the default RGB color model and |
|
* default sRGB colorspace. |
|
* @see #setRGB(int, int, int) |
|
* @see #setRGB(int, int, int, int, int[], int, int) |
|
*/ |
|
public int getRGB(int x, int y) { |
|
return colorModel.getRGB(raster.getDataElements(x, y, null)); |
|
} |
|
/** |
|
* Returns an array of integer pixels in the default RGB color model |
|
* (TYPE_INT_ARGB) and default sRGB color space, |
|
* from a portion of the image data. Color conversion takes |
|
* place if the default model does not match the image |
|
* <code>ColorModel</code>. There are only 8-bits of precision for |
|
* each color component in the returned data when |
|
* using this method. With a specified coordinate (x, y) in the |
|
* image, the ARGB pixel can be accessed in this way: |
|
* |
|
* <pre> |
|
* pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre> |
|
* |
|
* <p> |
|
* |
|
* An <code>ArrayOutOfBoundsException</code> may be thrown |
|
* if the region is not in bounds. |
|
* However, explicit bounds checking is not guaranteed. |
|
* |
|
* @param startX the starting X coordinate |
|
* @param startY the starting Y coordinate |
|
* @param w width of region |
|
* @param h height of region |
|
* @param rgbArray if not <code>null</code>, the rgb pixels are |
|
* written here |
|
* @param offset offset into the <code>rgbArray</code> |
|
* @param scansize scanline stride for the <code>rgbArray</code> |
|
* @return array of RGB pixels. |
|
* @see #setRGB(int, int, int) |
|
* @see #setRGB(int, int, int, int, int[], int, int) |
|
*/ |
|
public int[] getRGB(int startX, int startY, int w, int h, |
|
int[] rgbArray, int offset, int scansize) { |
|
int yoff = offset; |
|
int off; |
|
Object data; |
|
int nbands = raster.getNumBands(); |
|
int dataType = raster.getDataBuffer().getDataType(); |
|
switch (dataType) { |
|
case DataBuffer.TYPE_BYTE: |
|
data = new byte[nbands]; |
|
break; |
|
case DataBuffer.TYPE_USHORT: |
|
data = new short[nbands]; |
|
break; |
|
case DataBuffer.TYPE_INT: |
|
data = new int[nbands]; |
|
break; |
|
case DataBuffer.TYPE_FLOAT: |
|
data = new float[nbands]; |
|
break; |
|
case DataBuffer.TYPE_DOUBLE: |
|
data = new double[nbands]; |
|
break; |
|
default: |
|
throw new IllegalArgumentException("Unknown data buffer type: "+ |
|
dataType); |
|
} |
|
if (rgbArray == null) { |
|
rgbArray = new int[offset+h*scansize]; |
|
} |
|
for (int y = startY; y < startY+h; y++, yoff+=scansize) { |
|
off = yoff; |
|
for (int x = startX; x < startX+w; x++) { |
|
rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x, |
|
y, |
|
data)); |
|
} |
|
} |
|
return rgbArray; |
|
} |
|
/** |
|
* Sets a pixel in this <code>BufferedImage</code> to the specified |
|
* RGB value. The pixel is assumed to be in the default RGB color |
|
* model, TYPE_INT_ARGB, and default sRGB color space. For images |
|
* with an <code>IndexColorModel</code>, the index with the nearest |
|
* color is chosen. |
|
* |
|
* <p> |
|
* |
|
* An <code>ArrayOutOfBoundsException</code> may be thrown |
|
* if the coordinates are not in bounds. |
|
* However, explicit bounds checking is not guaranteed. |
|
* |
|
* @param x the X coordinate of the pixel to set |
|
* @param y the Y coordinate of the pixel to set |
|
* @param rgb the RGB value |
|
* @see #getRGB(int, int) |
|
* @see #getRGB(int, int, int, int, int[], int, int) |
|
*/ |
|
public synchronized void setRGB(int x, int y, int rgb) { |
|
raster.setDataElements(x, y, colorModel.getDataElements(rgb, null)); |
|
} |
|
/** |
|
* Sets an array of integer pixels in the default RGB color model |
|
* (TYPE_INT_ARGB) and default sRGB color space, |
|
* into a portion of the image data. Color conversion takes place |
|
* if the default model does not match the image |
|
* <code>ColorModel</code>. There are only 8-bits of precision for |
|
* each color component in the returned data when |
|
* using this method. With a specified coordinate (x, y) in the |
|
* this image, the ARGB pixel can be accessed in this way: |
|
* <pre> |
|
* pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; |
|
* </pre> |
|
* WARNING: No dithering takes place. |
|
* |
|
* <p> |
|
* |
|
* An <code>ArrayOutOfBoundsException</code> may be thrown |
|
* if the region is not in bounds. |
|
* However, explicit bounds checking is not guaranteed. |
|
* |
|
* @param startX the starting X coordinate |
|
* @param startY the starting Y coordinate |
|
* @param w width of the region |
|
* @param h height of the region |
|
* @param rgbArray the rgb pixels |
|
* @param offset offset into the <code>rgbArray</code> |
|
* @param scansize scanline stride for the <code>rgbArray</code> |
|
* @see #getRGB(int, int) |
|
* @see #getRGB(int, int, int, int, int[], int, int) |
|
*/ |
|
public void setRGB(int startX, int startY, int w, int h, |
|
int[] rgbArray, int offset, int scansize) { |
|
int yoff = offset; |
|
int off; |
|
Object pixel = null; |
|
for (int y = startY; y < startY+h; y++, yoff+=scansize) { |
|
off = yoff; |
|
for (int x = startX; x < startX+w; x++) { |
|
pixel = colorModel.getDataElements(rgbArray[off++], pixel); |
|
raster.setDataElements(x, y, pixel); |
|
} |
|
} |
|
} |
|
/** |
|
* Returns the width of the <code>BufferedImage</code>. |
|
* @return the width of this <code>BufferedImage</code> |
|
*/ |
|
public int getWidth() { |
|
return raster.getWidth(); |
|
} |
|
/** |
|
* Returns the height of the <code>BufferedImage</code>. |
|
* @return the height of this <code>BufferedImage</code> |
|
*/ |
|
public int getHeight() { |
|
return raster.getHeight(); |
|
} |
|
/** |
|
* Returns the width of the <code>BufferedImage</code>. |
|
* @param observer ignored |
|
* @return the width of this <code>BufferedImage</code> |
|
*/ |
|
public int getWidth(ImageObserver observer) { |
|
return raster.getWidth(); |
|
} |
|
/** |
|
* Returns the height of the <code>BufferedImage</code>. |
|
* @param observer ignored |
|
* @return the height of this <code>BufferedImage</code> |
|
*/ |
|
public int getHeight(ImageObserver observer) { |
|
return raster.getHeight(); |
|
} |
|
/** |
|
* Returns the object that produces the pixels for the image. |
|
* @return the {@link ImageProducer} that is used to produce the |
|
* pixels for this image. |
|
* @see ImageProducer |
|
*/ |
|
public ImageProducer getSource() { |
|
if (osis == null) { |
|
if (properties == null) { |
|
properties = new Hashtable(); |
|
} |
|
osis = new OffScreenImageSource(this, properties); |
|
} |
|
return osis; |
|
} |
|
/** |
|
* Returns a property of the image by name. Individual property names |
|
* are defined by the various image formats. If a property is not |
|
* defined for a particular image, this method returns the |
|
* <code>UndefinedProperty</code> field. If the properties |
|
* for this image are not yet known, then this method returns |
|
* <code>null</code> and the <code>ImageObserver</code> object is |
|
* notified later. The property name "comment" should be used to |
|
* store an optional comment that can be presented to the user as a |
|
* description of the image, its source, or its author. |
|
* @param name the property name |
|
* @param observer the <code>ImageObserver</code> that receives |
|
* notification regarding image information |
|
* @return an {@link Object} that is the property referred to by the |
|
* specified <code>name</code> or <code>null</code> if the |
|
* properties of this image are not yet known. |
|
* @throws NullPointerException if the property name is null. |
|
* @see ImageObserver |
|
* @see java.awt.Image#UndefinedProperty |
|
*/ |
|
public Object getProperty(String name, ImageObserver observer) { |
|
return getProperty(name); |
|
} |
|
/** |
|
* Returns a property of the image by name. |
|
* @param name the property name |
|
* @return an <code>Object</code> that is the property referred to by |
|
* the specified <code>name</code>. |
|
* @throws NullPointerException if the property name is null. |
|
*/ |
|
public Object getProperty(String name) { |
|
if (name == null) { |
|
throw new NullPointerException("null property name is not allowed"); |
|
} |
|
if (properties == null) { |
|
return java.awt.Image.UndefinedProperty; |
|
} |
|
Object o = properties.get(name); |
|
if (o == null) { |
|
o = java.awt.Image.UndefinedProperty; |
|
} |
|
return o; |
|
} |
|
/** |
|
* This method returns a {@link Graphics2D}, but is here |
|
* for backwards compatibility. {@link #createGraphics() createGraphics} is more |
|
* convenient, since it is declared to return a |
|
* <code>Graphics2D</code>. |
|
* @return a <code>Graphics2D</code>, which can be used to draw into |
|
* this image. |
|
*/ |
|
public java.awt.Graphics getGraphics() { |
|
return createGraphics(); |
|
} |
|
/** |
|
* Creates a <code>Graphics2D</code>, which can be used to draw into |
|
* this <code>BufferedImage</code>. |
|
* @return a <code>Graphics2D</code>, used for drawing into this |
|
* image. |
|
*/ |
|
public Graphics2D createGraphics() { |
|
GraphicsEnvironment env = |
|
GraphicsEnvironment.getLocalGraphicsEnvironment(); |
|
return env.createGraphics(this); |
|
} |
|
/** |
|
* Returns a subimage defined by a specified rectangular region. |
|
* The returned <code>BufferedImage</code> shares the same |
|
* data array as the original image. |
|
* @param x the X coordinate of the upper-left corner of the |
|
* specified rectangular region |
|
* @param y the Y coordinate of the upper-left corner of the |
|
* specified rectangular region |
|
* @param w the width of the specified rectangular region |
|
* @param h the height of the specified rectangular region |
|
* @return a <code>BufferedImage</code> that is the subimage of this |
|
* <code>BufferedImage</code>. |
|
* @exception RasterFormatException if the specified |
|
* area is not contained within this <code>BufferedImage</code>. |
|
*/ |
|
public BufferedImage getSubimage (int x, int y, int w, int h) { |
|
return new BufferedImage (colorModel, |
|
raster.createWritableChild(x, y, w, h, |
|
0, 0, null), |
|
colorModel.isAlphaPremultiplied(), |
|
properties); |
|
} |
|
/** |
|
* Returns whether or not the alpha has been premultiplied. It |
|
* returns <code>false</code> if there is no alpha. |
|
* @return <code>true</code> if the alpha has been premultiplied; |
|
* <code>false</code> otherwise. |
|
*/ |
|
public boolean isAlphaPremultiplied() { |
|
return colorModel.isAlphaPremultiplied(); |
|
} |
|
/** |
|
* Forces the data to match the state specified in the |
|
* <code>isAlphaPremultiplied</code> variable. It may multiply or |
|
* divide the color raster data by alpha, or do nothing if the data is |
|
* in the correct state. |
|
* @param isAlphaPremultiplied <code>true</code> if the alpha has been |
|
* premultiplied; <code>false</code> otherwise. |
|
*/ |
|
public void coerceData (boolean isAlphaPremultiplied) { |
|
if (colorModel.hasAlpha() && |
|
colorModel.isAlphaPremultiplied() != isAlphaPremultiplied) { |
|
// Make the color model do the conversion |
|
colorModel = colorModel.coerceData (raster, isAlphaPremultiplied); |
|
} |
|
} |
|
/** |
|
* Returns a <code>String</code> representation of this |
|
* <code>BufferedImage</code> object and its values. |
|
* @return a <code>String</code> representing this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public String toString() { |
|
return "BufferedImage@"+Integer.toHexString(hashCode()) |
|
+": type = "+imageType |
|
+" "+colorModel+" "+raster; |
|
} |
|
/** |
|
* Returns a {@link Vector} of {@link RenderedImage} objects that are |
|
* the immediate sources, not the sources of these immediate sources, |
|
* of image data for this <code>BufferedImage</code>. This |
|
* method returns <code>null</code> if the <code>BufferedImage</code> |
|
* has no information about its immediate sources. It returns an |
|
* empty <code>Vector</code> if the <code>BufferedImage</code> has no |
|
* immediate sources. |
|
* @return a <code>Vector</code> containing immediate sources of |
|
* this <code>BufferedImage</code> object's image date, or |
|
* <code>null</code> if this <code>BufferedImage</code> has |
|
* no information about its immediate sources, or an empty |
|
* <code>Vector</code> if this <code>BufferedImage</code> |
|
* has no immediate sources. |
|
*/ |
|
public Vector<RenderedImage> getSources() { |
|
return null; |
|
} |
|
/** |
|
* Returns an array of names recognized by |
|
* {@link #getProperty(String) getProperty(String)} |
|
* or <code>null</code>, if no property names are recognized. |
|
* @return a <code>String</code> array containing all of the property |
|
* names that <code>getProperty(String)</code> recognizes; |
|
* or <code>null</code> if no property names are recognized. |
|
*/ |
|
public String[] getPropertyNames() { |
|
if (properties == null || properties.isEmpty()) { |
|
return null; |
|
} |
|
final Set<String> keys = properties.keySet(); |
|
return keys.toArray(new String[keys.size()]); |
|
} |
|
/** |
|
* Returns the minimum x coordinate of this |
|
* <code>BufferedImage</code>. This is always zero. |
|
* @return the minimum x coordinate of this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public int getMinX() { |
|
return raster.getMinX(); |
|
} |
|
/** |
|
* Returns the minimum y coordinate of this |
|
* <code>BufferedImage</code>. This is always zero. |
|
* @return the minimum y coordinate of this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public int getMinY() { |
|
return raster.getMinY(); |
|
} |
|
/** |
|
* Returns the <code>SampleModel</code> associated with this |
|
* <code>BufferedImage</code>. |
|
* @return the <code>SampleModel</code> of this |
|
* <code>BufferedImage</code>. |
|
*/ |
|
public SampleModel getSampleModel() { |
|
return raster.getSampleModel(); |
|
} |
|
/** |
|
* Returns the number of tiles in the x direction. |
|
* This is always one. |
|
* @return the number of tiles in the x direction. |
|
*/ |
|
public int getNumXTiles() { |
|
return 1; |
|
} |
|
/** |
|
* Returns the number of tiles in the y direction. |
|
* This is always one. |
|
* @return the number of tiles in the y direction. |
|
*/ |
|
public int getNumYTiles() { |
|
return 1; |
|
} |
|
/** |
|
* Returns the minimum tile index in the x direction. |
|
* This is always zero. |
|
* @return the minimum tile index in the x direction. |
|
*/ |
|
public int getMinTileX() { |
|
return 0; |
|
} |
|
/** |
|
* Returns the minimum tile index in the y direction. |
|
* This is always zero. |
|
* @return the minimum tile index in the y direction. |
|
*/ |
|
public int getMinTileY() { |
|
return 0; |
|
} |
|
/** |
|
* Returns the tile width in pixels. |
|
* @return the tile width in pixels. |
|
*/ |
|
public int getTileWidth() { |
|
return raster.getWidth(); |
|
} |
|
/** |
|
* Returns the tile height in pixels. |
|
* @return the tile height in pixels. |
|
*/ |
|
public int getTileHeight() { |
|
return raster.getHeight(); |
|
} |
|
/** |
|
* Returns the x offset of the tile grid relative to the origin, |
|
* For example, the x coordinate of the location of tile |
|
* (0, 0). This is always zero. |
|
* @return the x offset of the tile grid. |
|
*/ |
|
public int getTileGridXOffset() { |
|
return raster.getSampleModelTranslateX(); |
|
} |
|
/** |
|
* Returns the y offset of the tile grid relative to the origin, |
|
* For example, the y coordinate of the location of tile |
|
* (0, 0). This is always zero. |
|
* @return the y offset of the tile grid. |
|
*/ |
|
public int getTileGridYOffset() { |
|
return raster.getSampleModelTranslateY(); |
|
} |
|
/** |
|
* Returns tile (<code>tileX</code>, <code>tileY</code>). Note |
|
* that <code>tileX</code> and <code>tileY</code> are indices |
|
* into the tile array, not pixel locations. The <code>Raster</code> |
|
* that is returned is live, which means that it is updated if the |
|
* image is changed. |
|
* @param tileX the x index of the requested tile in the tile array |
|
* @param tileY the y index of the requested tile in the tile array |
|
* @return a <code>Raster</code> that is the tile defined by the |
|
* arguments <code>tileX</code> and <code>tileY</code>. |
|
* @exception ArrayIndexOutOfBoundsException if both |
|
* <code>tileX</code> and <code>tileY</code> are not |
|
* equal to 0 |
|
*/ |
|
public Raster getTile(int tileX, int tileY) { |
|
if (tileX == 0 && tileY == 0) { |
|
return raster; |
|
} |
|
throw new ArrayIndexOutOfBoundsException("BufferedImages only have"+ |
|
" one tile with index 0,0"); |
|
} |
|
/** |
|
* Returns the image as one large tile. The <code>Raster</code> |
|
* returned is a copy of the image data is not updated if the |
|
* image is changed. |
|
* @return a <code>Raster</code> that is a copy of the image data. |
|
* @see #setData(Raster) |
|
*/ |
|
public Raster getData() { |
|
// REMIND : this allocates a whole new tile if raster is a |
|
// subtile. (It only copies in the requested area) |
|
// We should do something smarter. |
|
int width = raster.getWidth(); |
|
int height = raster.getHeight(); |
|
int startX = raster.getMinX(); |
|
int startY = raster.getMinY(); |
|
WritableRaster wr = |
|
Raster.createWritableRaster(raster.getSampleModel(), |
|
new Point(raster.getSampleModelTranslateX(), |
|
raster.getSampleModelTranslateY())); |
|
Object tdata = null; |
|
for (int i = startY; i < startY+height; i++) { |
|
tdata = raster.getDataElements(startX,i,width,1,tdata); |
|
wr.setDataElements(startX,i,width,1, tdata); |
|
} |
|
return wr; |
|
} |
|
/** |
|
* Computes and returns an arbitrary region of the |
|
* <code>BufferedImage</code>. The <code>Raster</code> returned is a |
|
* copy of the image data and is not updated if the image is |
|
* changed. |
|
* @param rect the region of the <code>BufferedImage</code> to be |
|
* returned. |
|
* @return a <code>Raster</code> that is a copy of the image data of |
|
* the specified region of the <code>BufferedImage</code> |
|
* @see #setData(Raster) |
|
*/ |
|
public Raster getData(Rectangle rect) { |
|
SampleModel sm = raster.getSampleModel(); |
|
SampleModel nsm = sm.createCompatibleSampleModel(rect.width, |
|
rect.height); |
|
WritableRaster wr = Raster.createWritableRaster(nsm, |
|
rect.getLocation()); |
|
int width = rect.width; |
|
int height = rect.height; |
|
int startX = rect.x; |
|
int startY = rect.y; |
|
Object tdata = null; |
|
for (int i = startY; i < startY+height; i++) { |
|
tdata = raster.getDataElements(startX,i,width,1,tdata); |
|
wr.setDataElements(startX,i,width,1, tdata); |
|
} |
|
return wr; |
|
} |
|
/** |
|
* Computes an arbitrary rectangular region of the |
|
* <code>BufferedImage</code> and copies it into a specified |
|
* <code>WritableRaster</code>. The region to be computed is |
|
* determined from the bounds of the specified |
|
* <code>WritableRaster</code>. The specified |
|
* <code>WritableRaster</code> must have a |
|
* <code>SampleModel</code> that is compatible with this image. If |
|
* <code>outRaster</code> is <code>null</code>, |
|
* an appropriate <code>WritableRaster</code> is created. |
|
* @param outRaster a <code>WritableRaster</code> to hold the returned |
|
* part of the image, or <code>null</code> |
|
* @return a reference to the supplied or created |
|
* <code>WritableRaster</code>. |
|
*/ |
|
public WritableRaster copyData(WritableRaster outRaster) { |
|
if (outRaster == null) { |
|
return (WritableRaster) getData(); |
|
} |
|
int width = outRaster.getWidth(); |
|
int height = outRaster.getHeight(); |
|
int startX = outRaster.getMinX(); |
|
int startY = outRaster.getMinY(); |
|
Object tdata = null; |
|
for (int i = startY; i < startY+height; i++) { |
|
tdata = raster.getDataElements(startX,i,width,1,tdata); |
|
outRaster.setDataElements(startX,i,width,1, tdata); |
|
} |
|
return outRaster; |
|
} |
|
/** |
|
* Sets a rectangular region of the image to the contents of the |
|
* specified <code>Raster</code> <code>r</code>, which is |
|
* assumed to be in the same coordinate space as the |
|
* <code>BufferedImage</code>. The operation is clipped to the bounds |
|
* of the <code>BufferedImage</code>. |
|
* @param r the specified <code>Raster</code> |
|
* @see #getData |
|
* @see #getData(Rectangle) |
|
*/ |
|
public void setData(Raster r) { |
|
int width = r.getWidth(); |
|
int height = r.getHeight(); |
|
int startX = r.getMinX(); |
|
int startY = r.getMinY(); |
|
int[] tdata = null; |
|
// Clip to the current Raster |
|
Rectangle rclip = new Rectangle(startX, startY, width, height); |
|
Rectangle bclip = new Rectangle(0, 0, raster.width, raster.height); |
|
Rectangle intersect = rclip.intersection(bclip); |
|
if (intersect.isEmpty()) { |
|
return; |
|
} |
|
width = intersect.width; |
|
height = intersect.height; |
|
startX = intersect.x; |
|
startY = intersect.y; |
|
// remind use get/setDataElements for speed if Rasters are |
|
// compatible |
|
for (int i = startY; i < startY+height; i++) { |
|
tdata = r.getPixels(startX,i,width,1,tdata); |
|
raster.setPixels(startX,i,width,1, tdata); |
|
} |
|
} |
|
/** |
|
* Adds a tile observer. If the observer is already present, |
|
* it receives multiple notifications. |
|
* @param to the specified {@link TileObserver} |
|
*/ |
|
public void addTileObserver (TileObserver to) { |
|
} |
|
/** |
|
* Removes a tile observer. If the observer was not registered, |
|
* nothing happens. If the observer was registered for multiple |
|
* notifications, it is now registered for one fewer notification. |
|
* @param to the specified <code>TileObserver</code>. |
|
*/ |
|
public void removeTileObserver (TileObserver to) { |
|
} |
|
/** |
|
* Returns whether or not a tile is currently checked out for writing. |
|
* @param tileX the x index of the tile. |
|
* @param tileY the y index of the tile. |
|
* @return <code>true</code> if the tile specified by the specified |
|
* indices is checked out for writing; <code>false</code> |
|
* otherwise. |
|
* @exception ArrayIndexOutOfBoundsException if both |
|
* <code>tileX</code> and <code>tileY</code> are not equal |
|
* to 0 |
|
*/ |
|
public boolean isTileWritable (int tileX, int tileY) { |
|
if (tileX == 0 && tileY == 0) { |
|
return true; |
|
} |
|
throw new IllegalArgumentException("Only 1 tile in image"); |
|
} |
|
/** |
|
* Returns an array of {@link Point} objects indicating which tiles |
|
* are checked out for writing. Returns <code>null</code> if none are |
|
* checked out. |
|
* @return a <code>Point</code> array that indicates the tiles that |
|
* are checked out for writing, or <code>null</code> if no |
|
* tiles are checked out for writing. |
|
*/ |
|
public Point[] getWritableTileIndices() { |
|
Point[] p = new Point[1]; |
|
p[0] = new Point(0, 0); |
|
return p; |
|
} |
|
/** |
|
* Returns whether or not any tile is checked out for writing. |
|
* Semantically equivalent to |
|
* <pre> |
|
* (getWritableTileIndices() != null). |
|
* </pre> |
|
* @return <code>true</code> if any tile is checked out for writing; |
|
* <code>false</code> otherwise. |
|
*/ |
|
public boolean hasTileWriters () { |
|
return true; |
|
} |
|
/** |
|
* Checks out a tile for writing. All registered |
|
* <code>TileObservers</code> are notified when a tile goes from having |
|
* no writers to having one writer. |
|
* @param tileX the x index of the tile |
|
* @param tileY the y index of the tile |
|
* @return a <code>WritableRaster</code> that is the tile, indicated by |
|
* the specified indices, to be checked out for writing. |
|
*/ |
|
public WritableRaster getWritableTile (int tileX, int tileY) { |
|
return raster; |
|
} |
|
/** |
|
* Relinquishes permission to write to a tile. If the caller |
|
* continues to write to the tile, the results are undefined. |
|
* Calls to this method should only appear in matching pairs |
|
* with calls to {@link #getWritableTile(int, int) getWritableTile(int, int)}. Any other leads |
|
* to undefined results. All registered <code>TileObservers</code> |
|
* are notified when a tile goes from having one writer to having no |
|
* writers. |
|
* @param tileX the x index of the tile |
|
* @param tileY the y index of the tile |
|
*/ |
|
public void releaseWritableTile (int tileX, int tileY) { |
|
} |
|
/** |
|
* Returns the transparency. Returns either OPAQUE, BITMASK, |
|
* or TRANSLUCENT. |
|
* @return the transparency of this <code>BufferedImage</code>. |
|
* @see Transparency#OPAQUE |
|
* @see Transparency#BITMASK |
|
* @see Transparency#TRANSLUCENT |
|
* @since 1.5 |
|
*/ |
|
public int getTransparency() { |
|
return colorModel.getTransparency(); |
|
} |
|
} |