/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.imageio;
import java.awt.
Point;
import java.awt.
Transparency;
import java.awt.image.
BandedSampleModel;
import java.awt.image.
BufferedImage;
import java.awt.image.
ColorModel;
import java.awt.color.
ColorSpace;
import java.awt.image.
IndexColorModel;
import java.awt.image.
ComponentColorModel;
import java.awt.image.
DataBuffer;
import java.awt.image.
DirectColorModel;
import java.awt.image.
MultiPixelPackedSampleModel;
import java.awt.image.
PixelInterleavedSampleModel;
import java.awt.image.
SinglePixelPackedSampleModel;
import java.awt.image.
Raster;
import java.awt.image.
RenderedImage;
import java.awt.image.
SampleModel;
import java.awt.image.
WritableRaster;
import java.util.
Hashtable;
/**
* A class that allows the format of an image (in particular, its
* <code>SampleModel</code> and <code>ColorModel</code>) to be
* specified in a convenient manner.
*
*/
public class
ImageTypeSpecifier {
/**
* The <code>ColorModel</code> to be used as a prototype.
*/
protected
ColorModel colorModel;
/**
* A <code>SampleModel</code> to be used as a prototype.
*/
protected
SampleModel sampleModel;
/**
* Cached specifiers for all of the standard
* <code>BufferedImage</code> types.
*/
private static
ImageTypeSpecifier[]
BISpecifier;
private static
ColorSpace sRGB;
// Initialize the standard specifiers
static {
sRGB =
ColorSpace.
getInstance(
ColorSpace.
CS_sRGB);
BISpecifier =
new
ImageTypeSpecifier[
BufferedImage.
TYPE_BYTE_INDEXED + 1];
}
/**
* A constructor to be used by inner subclasses only.
*/
private
ImageTypeSpecifier() {}
/**
* Constructs an <code>ImageTypeSpecifier</code> directly
* from a <code>ColorModel</code> and a <code>SampleModel</code>.
* It is the caller's responsibility to supply compatible
* parameters.
*
* @param colorModel a <code>ColorModel</code>.
* @param sampleModel a <code>SampleModel</code>.
*
* @exception IllegalArgumentException if either parameter is
* <code>null</code>.
* @exception IllegalArgumentException if <code>sampleModel</code>
* is not compatible with <code>colorModel</code>.
*/
public
ImageTypeSpecifier(
ColorModel colorModel,
SampleModel sampleModel) {
if (
colorModel == null) {
throw new
IllegalArgumentException("colorModel == null!");
}
if (
sampleModel == null) {
throw new
IllegalArgumentException("sampleModel == null!");
}
if (!
colorModel.
isCompatibleSampleModel(
sampleModel)) {
throw new
IllegalArgumentException
("sampleModel is incompatible with colorModel!");
}
this.
colorModel =
colorModel;
this.
sampleModel =
sampleModel;
}
/**
* Constructs an <code>ImageTypeSpecifier</code> from a
* <code>RenderedImage</code>. If a <code>BufferedImage</code> is
* being used, one of the factory methods
* <code>createFromRenderedImage</code> or
* <code>createFromBufferedImageType</code> should be used instead in
* order to get a more accurate result.
*
* @param image a <code>RenderedImage</code>.
*
* @exception IllegalArgumentException if the argument is
* <code>null</code>.
*/
public
ImageTypeSpecifier(
RenderedImage image) {
if (
image == null) {
throw new
IllegalArgumentException("image == null!");
}
colorModel =
image.
getColorModel();
sampleModel =
image.
getSampleModel();
}
// Packed
static class
Packed extends
ImageTypeSpecifier {
ColorSpace colorSpace;
int
redMask;
int
greenMask;
int
blueMask;
int
alphaMask;
int
transferType;
boolean
isAlphaPremultiplied;
public
Packed(
ColorSpace colorSpace,
int
redMask,
int
greenMask,
int
blueMask,
int
alphaMask, // 0 if no alpha
int
transferType,
boolean
isAlphaPremultiplied) {
if (
colorSpace == null) {
throw new
IllegalArgumentException("colorSpace == null!");
}
if (
colorSpace.
getType() !=
ColorSpace.
TYPE_RGB) {
throw new
IllegalArgumentException
("colorSpace is not of type TYPE_RGB!");
}
if (
transferType !=
DataBuffer.
TYPE_BYTE &&
transferType !=
DataBuffer.
TYPE_USHORT &&
transferType !=
DataBuffer.
TYPE_INT) {
throw new
IllegalArgumentException
("Bad value for transferType!");
}
if (
redMask == 0 &&
greenMask == 0 &&
blueMask == 0 &&
alphaMask == 0) {
throw new
IllegalArgumentException
("No mask has at least 1 bit set!");
}
this.
colorSpace =
colorSpace;
this.
redMask =
redMask;
this.
greenMask =
greenMask;
this.
blueMask =
blueMask;
this.
alphaMask =
alphaMask;
this.
transferType =
transferType;
this.
isAlphaPremultiplied =
isAlphaPremultiplied;
int
bits = 32;
this.
colorModel =
new
DirectColorModel(
colorSpace,
bits,
redMask,
greenMask,
blueMask,
alphaMask,
isAlphaPremultiplied,
transferType);
this.
sampleModel =
colorModel.
createCompatibleSampleModel(1, 1);
}
}
/**
* Returns a specifier for a packed image format that will use a
* <code>DirectColorModel</code> and a packed
* <code>SampleModel</code> to store each pixel packed into in a
* single byte, short, or int.
*
* @param colorSpace the desired <code>ColorSpace</code>.
* @param redMask a contiguous mask indicated the position of the
* red channel.
* @param greenMask a contiguous mask indicated the position of the
* green channel.
* @param blueMask a contiguous mask indicated the position of the
* blue channel.
* @param alphaMask a contiguous mask indicated the position of the
* alpha channel.
* @param transferType the desired <code>SampleModel</code> transfer type.
* @param isAlphaPremultiplied <code>true</code> if the color channels
* will be premultipled by the alpha channel.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>colorSpace</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>colorSpace</code>
* is not of type <code>TYPE_RGB</code>.
* @exception IllegalArgumentException if no mask has at least 1
* bit set.
* @exception IllegalArgumentException if
* <code>transferType</code> if not one of
* <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_USHORT</code>, or
* <code>DataBuffer.TYPE_INT</code>.
*/
public static
ImageTypeSpecifier
createPacked(
ColorSpace colorSpace,
int
redMask,
int
greenMask,
int
blueMask,
int
alphaMask, // 0 if no alpha
int
transferType,
boolean
isAlphaPremultiplied) {
return new
ImageTypeSpecifier.
Packed(
colorSpace,
redMask,
greenMask,
blueMask,
alphaMask, // 0 if no alpha
transferType,
isAlphaPremultiplied);
}
static
ColorModel createComponentCM(
ColorSpace colorSpace,
int
numBands,
int
dataType,
boolean
hasAlpha,
boolean
isAlphaPremultiplied) {
int
transparency =
hasAlpha ?
Transparency.
TRANSLUCENT :
Transparency.
OPAQUE;
int[]
numBits = new int[
numBands];
int
bits =
DataBuffer.
getDataTypeSize(
dataType);
for (int
i = 0;
i <
numBands;
i++) {
numBits[
i] =
bits;
}
return new
ComponentColorModel(
colorSpace,
numBits,
hasAlpha,
isAlphaPremultiplied,
transparency,
dataType);
}
// Interleaved
static class
Interleaved extends
ImageTypeSpecifier {
ColorSpace colorSpace;
int[]
bandOffsets;
int
dataType;
boolean
hasAlpha;
boolean
isAlphaPremultiplied;
public
Interleaved(
ColorSpace colorSpace,
int[]
bandOffsets,
int
dataType,
boolean
hasAlpha,
boolean
isAlphaPremultiplied) {
if (
colorSpace == null) {
throw new
IllegalArgumentException("colorSpace == null!");
}
if (
bandOffsets == null) {
throw new
IllegalArgumentException("bandOffsets == null!");
}
int
numBands =
colorSpace.
getNumComponents() +
(
hasAlpha ? 1 : 0);
if (
bandOffsets.length !=
numBands) {
throw new
IllegalArgumentException
("bandOffsets.length is wrong!");
}
if (
dataType !=
DataBuffer.
TYPE_BYTE &&
dataType !=
DataBuffer.
TYPE_SHORT &&
dataType !=
DataBuffer.
TYPE_USHORT &&
dataType !=
DataBuffer.
TYPE_INT &&
dataType !=
DataBuffer.
TYPE_FLOAT &&
dataType !=
DataBuffer.
TYPE_DOUBLE) {
throw new
IllegalArgumentException
("Bad value for dataType!");
}
this.
colorSpace =
colorSpace;
this.
bandOffsets = (int[])
bandOffsets.
clone();
this.
dataType =
dataType;
this.
hasAlpha =
hasAlpha;
this.
isAlphaPremultiplied =
isAlphaPremultiplied;
this.
colorModel =
ImageTypeSpecifier.
createComponentCM(
colorSpace,
bandOffsets.length,
dataType,
hasAlpha,
isAlphaPremultiplied);
int
minBandOffset =
bandOffsets[0];
int
maxBandOffset =
minBandOffset;
for (int
i = 0;
i <
bandOffsets.length;
i++) {
int
offset =
bandOffsets[
i];
minBandOffset =
Math.
min(
offset,
minBandOffset);
maxBandOffset =
Math.
max(
offset,
maxBandOffset);
}
int
pixelStride =
maxBandOffset -
minBandOffset + 1;
int
w = 1;
int
h = 1;
this.
sampleModel =
new
PixelInterleavedSampleModel(
dataType,
w,
h,
pixelStride,
w*
pixelStride,
bandOffsets);
}
public boolean
equals(
Object o) {
if ((
o == null) ||
!(
o instanceof
ImageTypeSpecifier.
Interleaved)) {
return false;
}
ImageTypeSpecifier.
Interleaved that =
(
ImageTypeSpecifier.
Interleaved)
o;
if ((!(this.
colorSpace.
equals(
that.
colorSpace))) ||
(this.
dataType !=
that.
dataType) ||
(this.
hasAlpha !=
that.
hasAlpha) ||
(this.
isAlphaPremultiplied !=
that.
isAlphaPremultiplied) ||
(this.
bandOffsets.length !=
that.
bandOffsets.length)) {
return false;
}
for (int
i = 0;
i <
bandOffsets.length;
i++) {
if (this.
bandOffsets[
i] !=
that.
bandOffsets[
i]) {
return false;
}
}
return true;
}
public int
hashCode() {
return (super.hashCode() +
(4 *
bandOffsets.length) +
(25 *
dataType) +
(
hasAlpha ? 17 : 18));
}
}
/**
* Returns a specifier for an interleaved image format that will
* use a <code>ComponentColorModel</code> and a
* <code>PixelInterleavedSampleModel</code> to store each pixel
* component in a separate byte, short, or int.
*
* @param colorSpace the desired <code>ColorSpace</code>.
* @param bandOffsets an array of <code>int</code>s indicating the
* offsets for each band.
* @param dataType the desired data type, as one of the enumerations
* from the <code>DataBuffer</code> class.
* @param hasAlpha <code>true</code> if an alpha channel is desired.
* @param isAlphaPremultiplied <code>true</code> if the color channels
* will be premultipled by the alpha channel.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>colorSpace</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>bandOffsets</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>dataType</code> is
* not one of the legal <code>DataBuffer.TYPE_*</code> constants.
* @exception IllegalArgumentException if
* <code>bandOffsets.length</code> does not equal the number of
* color space components, plus 1 if <code>hasAlpha</code> is
* <code>true</code>.
*/
public static
ImageTypeSpecifier
createInterleaved(
ColorSpace colorSpace,
int[]
bandOffsets,
int
dataType,
boolean
hasAlpha,
boolean
isAlphaPremultiplied) {
return new
ImageTypeSpecifier.
Interleaved(
colorSpace,
bandOffsets,
dataType,
hasAlpha,
isAlphaPremultiplied);
}
// Banded
static class
Banded extends
ImageTypeSpecifier {
ColorSpace colorSpace;
int[]
bankIndices;
int[]
bandOffsets;
int
dataType;
boolean
hasAlpha;
boolean
isAlphaPremultiplied;
public
Banded(
ColorSpace colorSpace,
int[]
bankIndices,
int[]
bandOffsets,
int
dataType,
boolean
hasAlpha,
boolean
isAlphaPremultiplied) {
if (
colorSpace == null) {
throw new
IllegalArgumentException("colorSpace == null!");
}
if (
bankIndices == null) {
throw new
IllegalArgumentException("bankIndices == null!");
}
if (
bandOffsets == null) {
throw new
IllegalArgumentException("bandOffsets == null!");
}
if (
bankIndices.length !=
bandOffsets.length) {
throw new
IllegalArgumentException
("bankIndices.length != bandOffsets.length!");
}
if (
dataType !=
DataBuffer.
TYPE_BYTE &&
dataType !=
DataBuffer.
TYPE_SHORT &&
dataType !=
DataBuffer.
TYPE_USHORT &&
dataType !=
DataBuffer.
TYPE_INT &&
dataType !=
DataBuffer.
TYPE_FLOAT &&
dataType !=
DataBuffer.
TYPE_DOUBLE) {
throw new
IllegalArgumentException
("Bad value for dataType!");
}
int
numBands =
colorSpace.
getNumComponents() +
(
hasAlpha ? 1 : 0);
if (
bandOffsets.length !=
numBands) {
throw new
IllegalArgumentException
("bandOffsets.length is wrong!");
}
this.
colorSpace =
colorSpace;
this.
bankIndices = (int[])
bankIndices.
clone();
this.
bandOffsets = (int[])
bandOffsets.
clone();
this.
dataType =
dataType;
this.
hasAlpha =
hasAlpha;
this.
isAlphaPremultiplied =
isAlphaPremultiplied;
this.
colorModel =
ImageTypeSpecifier.
createComponentCM(
colorSpace,
bankIndices.length,
dataType,
hasAlpha,
isAlphaPremultiplied);
int
w = 1;
int
h = 1;
this.
sampleModel = new
BandedSampleModel(
dataType,
w,
h,
w,
bankIndices,
bandOffsets);
}
public boolean
equals(
Object o) {
if ((
o == null) ||
!(
o instanceof
ImageTypeSpecifier.
Banded)) {
return false;
}
ImageTypeSpecifier.
Banded that =
(
ImageTypeSpecifier.
Banded)
o;
if ((!(this.
colorSpace.
equals(
that.
colorSpace))) ||
(this.
dataType !=
that.
dataType) ||
(this.
hasAlpha !=
that.
hasAlpha) ||
(this.
isAlphaPremultiplied !=
that.
isAlphaPremultiplied) ||
(this.
bankIndices.length !=
that.
bankIndices.length) ||
(this.
bandOffsets.length !=
that.
bandOffsets.length)) {
return false;
}
for (int
i = 0;
i <
bankIndices.length;
i++) {
if (this.
bankIndices[
i] !=
that.
bankIndices[
i]) {
return false;
}
}
for (int
i = 0;
i <
bandOffsets.length;
i++) {
if (this.
bandOffsets[
i] !=
that.
bandOffsets[
i]) {
return false;
}
}
return true;
}
public int
hashCode() {
return (super.hashCode() +
(3 *
bandOffsets.length) +
(7 *
bankIndices.length) +
(21 *
dataType) +
(
hasAlpha ? 19 : 29));
}
}
/**
* Returns a specifier for a banded image format that will use a
* <code>ComponentColorModel</code> and a
* <code>BandedSampleModel</code> to store each channel in a
* separate array.
*
* @param colorSpace the desired <code>ColorSpace</code>.
* @param bankIndices an array of <code>int</code>s indicating the
* bank in which each band will be stored.
* @param bandOffsets an array of <code>int</code>s indicating the
* starting offset of each band within its bank.
* @param dataType the desired data type, as one of the enumerations
* from the <code>DataBuffer</code> class.
* @param hasAlpha <code>true</code> if an alpha channel is desired.
* @param isAlphaPremultiplied <code>true</code> if the color channels
* will be premultipled by the alpha channel.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>colorSpace</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>bankIndices</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>bandOffsets</code>
* is <code>null</code>.
* @exception IllegalArgumentException if the lengths of
* <code>bankIndices</code> and <code>bandOffsets</code> differ.
* @exception IllegalArgumentException if
* <code>bandOffsets.length</code> does not equal the number of
* color space components, plus 1 if <code>hasAlpha</code> is
* <code>true</code>.
* @exception IllegalArgumentException if <code>dataType</code> is
* not one of the legal <code>DataBuffer.TYPE_*</code> constants.
*/
public static
ImageTypeSpecifier
createBanded(
ColorSpace colorSpace,
int[]
bankIndices,
int[]
bandOffsets,
int
dataType,
boolean
hasAlpha,
boolean
isAlphaPremultiplied) {
return new
ImageTypeSpecifier.
Banded(
colorSpace,
bankIndices,
bandOffsets,
dataType,
hasAlpha,
isAlphaPremultiplied);
}
// Grayscale
static class
Grayscale extends
ImageTypeSpecifier {
int
bits;
int
dataType;
boolean
isSigned;
boolean
hasAlpha;
boolean
isAlphaPremultiplied;
public
Grayscale(int
bits,
int
dataType,
boolean
isSigned,
boolean
hasAlpha,
boolean
isAlphaPremultiplied)
{
if (
bits != 1 &&
bits != 2 &&
bits != 4 &&
bits != 8 &&
bits != 16)
{
throw new
IllegalArgumentException("Bad value for bits!");
}
if (
dataType !=
DataBuffer.
TYPE_BYTE &&
dataType !=
DataBuffer.
TYPE_SHORT &&
dataType !=
DataBuffer.
TYPE_USHORT)
{
throw new
IllegalArgumentException
("Bad value for dataType!");
}
if (
bits > 8 &&
dataType ==
DataBuffer.
TYPE_BYTE) {
throw new
IllegalArgumentException
("Too many bits for dataType!");
}
this.
bits =
bits;
this.
dataType =
dataType;
this.
isSigned =
isSigned;
this.
hasAlpha =
hasAlpha;
this.
isAlphaPremultiplied =
isAlphaPremultiplied;
ColorSpace colorSpace =
ColorSpace.
getInstance(
ColorSpace.
CS_GRAY);
if ((
bits == 8 &&
dataType ==
DataBuffer.
TYPE_BYTE) ||
(
bits == 16 &&
(
dataType ==
DataBuffer.
TYPE_SHORT ||
dataType ==
DataBuffer.
TYPE_USHORT))) {
// Use component color model & sample model
int
numBands =
hasAlpha ? 2 : 1;
int
transparency =
hasAlpha ?
Transparency.
TRANSLUCENT :
Transparency.
OPAQUE;
int[]
nBits = new int[
numBands];
nBits[0] =
bits;
if (
numBands == 2) {
nBits[1] =
bits;
}
this.
colorModel =
new
ComponentColorModel(
colorSpace,
nBits,
hasAlpha,
isAlphaPremultiplied,
transparency,
dataType);
int[]
bandOffsets = new int[
numBands];
bandOffsets[0] = 0;
if (
numBands == 2) {
bandOffsets[1] = 1;
}
int
w = 1;
int
h = 1;
this.
sampleModel =
new
PixelInterleavedSampleModel(
dataType,
w,
h,
numBands,
w*
numBands,
bandOffsets);
} else {
int
numEntries = 1 <<
bits;
byte[]
arr = new byte[
numEntries];
for (int
i = 0;
i <
numEntries;
i++) {
arr[
i] = (byte)(
i*255/(
numEntries - 1));
}
this.
colorModel =
new
IndexColorModel(
bits,
numEntries,
arr,
arr,
arr);
this.
sampleModel =
new
MultiPixelPackedSampleModel(
dataType, 1, 1,
bits);
}
}
}
/**
* Returns a specifier for a grayscale image format that will pack
* pixels of the given bit depth into array elements of
* the specified data type.
*
* @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
* @param dataType the desired data type, as one of the enumerations
* from the <code>DataBuffer</code> class.
* @param isSigned <code>true</code> if negative values are to
* be represented.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>bits</code> is
* not one of 1, 2, 4, 8, or 16.
* @exception IllegalArgumentException if <code>dataType</code> is
* not one of <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_SHORT</code>, or
* <code>DataBuffer.TYPE_USHORT</code>.
* @exception IllegalArgumentException if <code>bits</code> is
* larger than the bit size of the given <code>dataType</code>.
*/
public static
ImageTypeSpecifier
createGrayscale(int
bits,
int
dataType,
boolean
isSigned) {
return new
ImageTypeSpecifier.
Grayscale(
bits,
dataType,
isSigned,
false,
false);
}
/**
* Returns a specifier for a grayscale plus alpha image format
* that will pack pixels of the given bit depth into array
* elements of the specified data type.
*
* @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
* @param dataType the desired data type, as one of the enumerations
* from the <code>DataBuffer</code> class.
* @param isSigned <code>true</code> if negative values are to
* be represented.
* @param isAlphaPremultiplied <code>true</code> if the luminance channel
* will be premultipled by the alpha channel.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>bits</code> is
* not one of 1, 2, 4, 8, or 16.
* @exception IllegalArgumentException if <code>dataType</code> is
* not one of <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_SHORT</code>, or
* <code>DataBuffer.TYPE_USHORT</code>.
* @exception IllegalArgumentException if <code>bits</code> is
* larger than the bit size of the given <code>dataType</code>.
*/
public static
ImageTypeSpecifier
createGrayscale(int
bits,
int
dataType,
boolean
isSigned,
boolean
isAlphaPremultiplied) {
return new
ImageTypeSpecifier.
Grayscale(
bits,
dataType,
isSigned,
true,
isAlphaPremultiplied);
}
// Indexed
static class
Indexed extends
ImageTypeSpecifier {
byte[]
redLUT;
byte[]
greenLUT;
byte[]
blueLUT;
byte[]
alphaLUT = null;
int
bits;
int
dataType;
public
Indexed(byte[]
redLUT,
byte[]
greenLUT,
byte[]
blueLUT,
byte[]
alphaLUT,
int
bits,
int
dataType) {
if (
redLUT == null ||
greenLUT == null ||
blueLUT == null) {
throw new
IllegalArgumentException("LUT is null!");
}
if (
bits != 1 &&
bits != 2 &&
bits != 4 &&
bits != 8 &&
bits != 16) {
throw new
IllegalArgumentException("Bad value for bits!");
}
if (
dataType !=
DataBuffer.
TYPE_BYTE &&
dataType !=
DataBuffer.
TYPE_SHORT &&
dataType !=
DataBuffer.
TYPE_USHORT &&
dataType !=
DataBuffer.
TYPE_INT) {
throw new
IllegalArgumentException
("Bad value for dataType!");
}
if ((
bits > 8 &&
dataType ==
DataBuffer.
TYPE_BYTE) ||
(
bits > 16 &&
dataType !=
DataBuffer.
TYPE_INT)) {
throw new
IllegalArgumentException
("Too many bits for dataType!");
}
int
len = 1 <<
bits;
if (
redLUT.length !=
len ||
greenLUT.length !=
len ||
blueLUT.length !=
len ||
(
alphaLUT != null &&
alphaLUT.length !=
len)) {
throw new
IllegalArgumentException("LUT has improper length!");
}
this.
redLUT = (byte[])
redLUT.
clone();
this.
greenLUT = (byte[])
greenLUT.
clone();
this.
blueLUT = (byte[])
blueLUT.
clone();
if (
alphaLUT != null) {
this.
alphaLUT = (byte[])
alphaLUT.
clone();
}
this.
bits =
bits;
this.
dataType =
dataType;
if (
alphaLUT == null) {
this.
colorModel = new
IndexColorModel(
bits,
redLUT.length,
redLUT,
greenLUT,
blueLUT);
} else {
this.
colorModel = new
IndexColorModel(
bits,
redLUT.length,
redLUT,
greenLUT,
blueLUT,
alphaLUT);
}
if ((
bits == 8 &&
dataType ==
DataBuffer.
TYPE_BYTE) ||
(
bits == 16 &&
(
dataType ==
DataBuffer.
TYPE_SHORT ||
dataType ==
DataBuffer.
TYPE_USHORT))) {
int[]
bandOffsets = { 0 };
this.
sampleModel =
new
PixelInterleavedSampleModel(
dataType,
1, 1, 1, 1,
bandOffsets);
} else {
this.
sampleModel =
new
MultiPixelPackedSampleModel(
dataType, 1, 1,
bits);
}
}
}
/**
* Returns a specifier for an indexed-color image format that will pack
* index values of the given bit depth into array elements of
* the specified data type.
*
* @param redLUT an array of <code>byte</code>s containing
* the red values for each index.
* @param greenLUT an array of <code>byte</code>s containing * the
* green values for each index.
* @param blueLUT an array of <code>byte</code>s containing the
* blue values for each index.
* @param alphaLUT an array of <code>byte</code>s containing the
* alpha values for each index, or <code>null</code> to create a
* fully opaque LUT.
* @param bits the number of bits in each index.
* @param dataType the desired output type, as one of the enumerations
* from the <code>DataBuffer</code> class.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>redLUT</code> is
* <code>null</code>.
* @exception IllegalArgumentException if <code>greenLUT</code> is
* <code>null</code>.
* @exception IllegalArgumentException if <code>blueLUT</code> is
* <code>null</code>.
* @exception IllegalArgumentException if <code>bits</code> is
* not one of 1, 2, 4, 8, or 16.
* @exception IllegalArgumentException if the
* non-<code>null</code> LUT parameters do not have lengths of
* exactly {@code 1 << bits}.
* @exception IllegalArgumentException if <code>dataType</code> is
* not one of <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_SHORT</code>,
* <code>DataBuffer.TYPE_USHORT</code>,
* or <code>DataBuffer.TYPE_INT</code>.
* @exception IllegalArgumentException if <code>bits</code> is
* larger than the bit size of the given <code>dataType</code>.
*/
public static
ImageTypeSpecifier
createIndexed(byte[]
redLUT,
byte[]
greenLUT,
byte[]
blueLUT,
byte[]
alphaLUT,
int
bits,
int
dataType) {
return new
ImageTypeSpecifier.
Indexed(
redLUT,
greenLUT,
blueLUT,
alphaLUT,
bits,
dataType);
}
/**
* Returns an <code>ImageTypeSpecifier</code> that encodes
* one of the standard <code>BufferedImage</code> types
* (other than <code>TYPE_CUSTOM</code>).
*
* @param bufferedImageType an int representing one of the standard
* <code>BufferedImage</code> types.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if
* <code>bufferedImageType</code> is not one of the standard
* types, or is equal to <code>TYPE_CUSTOM</code>.
*
* @see java.awt.image.BufferedImage
* @see java.awt.image.BufferedImage#TYPE_INT_RGB
* @see java.awt.image.BufferedImage#TYPE_INT_ARGB
* @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
* @see java.awt.image.BufferedImage#TYPE_INT_BGR
* @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
* @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
* @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
* @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
* @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
* @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
* @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
* @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
* @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
*/
public static
ImageTypeSpecifier createFromBufferedImageType(int
bufferedImageType) {
if (
bufferedImageType >=
BufferedImage.
TYPE_INT_RGB &&
bufferedImageType <=
BufferedImage.
TYPE_BYTE_INDEXED) {
return
getSpecifier(
bufferedImageType);
} else if (
bufferedImageType ==
BufferedImage.
TYPE_CUSTOM) {
throw new
IllegalArgumentException("Cannot create from TYPE_CUSTOM!");
} else {
throw new
IllegalArgumentException("Invalid BufferedImage type!");
}
}
/**
* Returns an <code>ImageTypeSpecifier</code> that encodes the
* layout of a <code>RenderedImage</code> (which may be a
* <code>BufferedImage</code>).
*
* @param image a <code>RenderedImage</code>.
*
* @return an <code>ImageTypeSpecifier</code> with the desired
* characteristics.
*
* @exception IllegalArgumentException if <code>image</code> is
* <code>null</code>.
*/
public static
ImageTypeSpecifier createFromRenderedImage(
RenderedImage image) {
if (
image == null) {
throw new
IllegalArgumentException("image == null!");
}
if (
image instanceof
BufferedImage) {
int
bufferedImageType = ((
BufferedImage)
image).
getType();
if (
bufferedImageType !=
BufferedImage.
TYPE_CUSTOM) {
return
getSpecifier(
bufferedImageType);
}
}
return new
ImageTypeSpecifier(
image);
}
/**
* Returns an int containing one of the enumerated constant values
* describing image formats from <code>BufferedImage</code>.
*
* @return an <code>int</code> representing a
* <code>BufferedImage</code> type.
*
* @see java.awt.image.BufferedImage
* @see java.awt.image.BufferedImage#TYPE_CUSTOM
* @see java.awt.image.BufferedImage#TYPE_INT_RGB
* @see java.awt.image.BufferedImage#TYPE_INT_ARGB
* @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
* @see java.awt.image.BufferedImage#TYPE_INT_BGR
* @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
* @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
* @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
* @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
* @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
* @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
* @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
* @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
* @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
*/
public int
getBufferedImageType() {
BufferedImage bi =
createBufferedImage(1, 1);
return
bi.
getType();
}
/**
* Return the number of color components
* specified by this object. This is the same value as returned by
* <code>ColorModel.getNumComponents</code>
*
* @return the number of components in the image.
*/
public int
getNumComponents() {
return
colorModel.
getNumComponents();
}
/**
* Return the number of bands
* specified by this object. This is the same value as returned by
* <code>SampleModel.getNumBands</code>
*
* @return the number of bands in the image.
*/
public int
getNumBands() {
return
sampleModel.
getNumBands();
}
/**
* Return the number of bits used to represent samples of the given band.
*
* @param band the index of the band to be queried, as an
* int.
*
* @return an int specifying a number of bits.
*
* @exception IllegalArgumentException if <code>band</code> is
* negative or greater than the largest band index.
*/
public int
getBitsPerBand(int
band) {
if (
band < 0 |
band >=
getNumBands()) {
throw new
IllegalArgumentException("band out of range!");
}
return
sampleModel.
getSampleSize(
band);
}
/**
* Returns a <code>SampleModel</code> based on the settings
* encapsulated within this object. The width and height of the
* <code>SampleModel</code> will be set to arbitrary values.
*
* @return a <code>SampleModel</code> with arbitrary dimensions.
*/
public
SampleModel getSampleModel() {
return
sampleModel;
}
/**
* Returns a <code>SampleModel</code> based on the settings
* encapsulated within this object. The width and height of the
* <code>SampleModel</code> will be set to the supplied values.
*
* @param width the desired width of the returned <code>SampleModel</code>.
* @param height the desired height of the returned
* <code>SampleModel</code>.
*
* @return a <code>SampleModel</code> with the given dimensions.
*
* @exception IllegalArgumentException if either <code>width</code> or
* <code>height</code> are negative or zero.
* @exception IllegalArgumentException if the product of
* <code>width</code> and <code>height</code> is greater than
* <code>Integer.MAX_VALUE</code>
*/
public
SampleModel getSampleModel(int
width, int
height) {
if ((long)
width*
height >
Integer.
MAX_VALUE) {
throw new
IllegalArgumentException
("width*height > Integer.MAX_VALUE!");
}
return
sampleModel.
createCompatibleSampleModel(
width,
height);
}
/**
* Returns the <code>ColorModel</code> specified by this object.
*
* @return a <code>ColorModel</code>.
*/
public
ColorModel getColorModel() {
return
colorModel;
}
/**
* Creates a <code>BufferedImage</code> with a given width and
* height according to the specification embodied in this object.
*
* @param width the desired width of the returned
* <code>BufferedImage</code>.
* @param height the desired height of the returned
* <code>BufferedImage</code>.
*
* @return a new <code>BufferedImage</code>
*
* @exception IllegalArgumentException if either <code>width</code> or
* <code>height</code> are negative or zero.
* @exception IllegalArgumentException if the product of
* <code>width</code> and <code>height</code> is greater than
* <code>Integer.MAX_VALUE</code>, or if the number of array
* elements needed to store the image is greater than
* <code>Integer.MAX_VALUE</code>.
*/
public
BufferedImage createBufferedImage(int
width, int
height) {
try {
SampleModel sampleModel =
getSampleModel(
width,
height);
WritableRaster raster =
Raster.
createWritableRaster(
sampleModel,
new
Point(0, 0));
return new
BufferedImage(
colorModel,
raster,
colorModel.
isAlphaPremultiplied(),
new
Hashtable());
} catch (
NegativeArraySizeException e) {
// Exception most likely thrown from a DataBuffer constructor
throw new
IllegalArgumentException
("Array size > Integer.MAX_VALUE!");
}
}
/**
* Returns <code>true</code> if the given <code>Object</code> is
* an <code>ImageTypeSpecifier</code> and has a
* <code>SampleModel</code> and <code>ColorModel</code> that are
* equal to those of this object.
*
* @param o the <code>Object</code> to be compared for equality.
*
* @return <code>true</code> if the given object is an equivalent
* <code>ImageTypeSpecifier</code>.
*/
public boolean
equals(
Object o) {
if ((
o == null) || !(
o instanceof
ImageTypeSpecifier)) {
return false;
}
ImageTypeSpecifier that = (
ImageTypeSpecifier)
o;
return (
colorModel.
equals(
that.
colorModel)) &&
(
sampleModel.
equals(
that.
sampleModel));
}
/**
* Returns the hash code for this ImageTypeSpecifier.
*
* @return a hash code for this ImageTypeSpecifier
*/
public int
hashCode() {
return (9 *
colorModel.
hashCode()) + (14 *
sampleModel.
hashCode());
}
private static
ImageTypeSpecifier getSpecifier(int
type) {
if (
BISpecifier[
type] == null) {
BISpecifier[
type] =
createSpecifier(
type);
}
return
BISpecifier[
type];
}
private static
ImageTypeSpecifier createSpecifier(int
type) {
switch(
type) {
case
BufferedImage.
TYPE_INT_RGB:
return
createPacked(
sRGB,
0x00ff0000,
0x0000ff00,
0x000000ff,
0x0,
DataBuffer.
TYPE_INT,
false);
case
BufferedImage.
TYPE_INT_ARGB:
return
createPacked(
sRGB,
0x00ff0000,
0x0000ff00,
0x000000ff,
0xff000000,
DataBuffer.
TYPE_INT,
false);
case
BufferedImage.
TYPE_INT_ARGB_PRE:
return
createPacked(
sRGB,
0x00ff0000,
0x0000ff00,
0x000000ff,
0xff000000,
DataBuffer.
TYPE_INT,
true);
case
BufferedImage.
TYPE_INT_BGR:
return
createPacked(
sRGB,
0x000000ff,
0x0000ff00,
0x00ff0000,
0x0,
DataBuffer.
TYPE_INT,
false);
case
BufferedImage.
TYPE_3BYTE_BGR:
return
createInterleaved(
sRGB,
new int[] { 2, 1, 0 },
DataBuffer.
TYPE_BYTE,
false,
false);
case
BufferedImage.
TYPE_4BYTE_ABGR:
return
createInterleaved(
sRGB,
new int[] { 3, 2, 1, 0 },
DataBuffer.
TYPE_BYTE,
true,
false);
case
BufferedImage.
TYPE_4BYTE_ABGR_PRE:
return
createInterleaved(
sRGB,
new int[] { 3, 2, 1, 0 },
DataBuffer.
TYPE_BYTE,
true,
true);
case
BufferedImage.
TYPE_USHORT_565_RGB:
return
createPacked(
sRGB,
0xF800,
0x07E0,
0x001F,
0x0,
DataBuffer.
TYPE_USHORT,
false);
case
BufferedImage.
TYPE_USHORT_555_RGB:
return
createPacked(
sRGB,
0x7C00,
0x03E0,
0x001F,
0x0,
DataBuffer.
TYPE_USHORT,
false);
case
BufferedImage.
TYPE_BYTE_GRAY:
return
createGrayscale(8,
DataBuffer.
TYPE_BYTE,
false);
case
BufferedImage.
TYPE_USHORT_GRAY:
return
createGrayscale(16,
DataBuffer.
TYPE_USHORT,
false);
case
BufferedImage.
TYPE_BYTE_BINARY:
return
createGrayscale(1,
DataBuffer.
TYPE_BYTE,
false);
case
BufferedImage.
TYPE_BYTE_INDEXED:
{
BufferedImage bi =
new
BufferedImage(1, 1,
BufferedImage.
TYPE_BYTE_INDEXED);
IndexColorModel icm = (
IndexColorModel)
bi.
getColorModel();
int
mapSize =
icm.
getMapSize();
byte[]
redLUT = new byte[
mapSize];
byte[]
greenLUT = new byte[
mapSize];
byte[]
blueLUT = new byte[
mapSize];
byte[]
alphaLUT = new byte[
mapSize];
icm.
getReds(
redLUT);
icm.
getGreens(
greenLUT);
icm.
getBlues(
blueLUT);
icm.
getAlphas(
alphaLUT);
return
createIndexed(
redLUT,
greenLUT,
blueLUT,
alphaLUT,
8,
DataBuffer.
TYPE_BYTE);
}
default:
throw new
IllegalArgumentException("Invalid BufferedImage type!");
}
}
}