/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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();
}
}