/* |
|
* Copyright (c) 2000, 2013, 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 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!"); |
|
} |
|
} |
|
} |