/* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.awt.image; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; /** * A {@code ColorModel} class that works with pixel values that * represent color and alpha information as separate samples and that * store each sample in a separate data element. This class can be * used with an arbitrary {@code ColorSpace}. The number of * color samples in the pixel values must be same as the number of * color components in the {@code ColorSpace}. There may be a * single alpha sample. *
* For those methods that use * a primitive array pixel representation of type {@code transferType}, * the array length is the same as the number of color and alpha samples. * Color samples are stored first in the array followed by the alpha * sample, if present. The order of the color samples is specified * by the {@code ColorSpace}. Typically, this order reflects the * name of the color space type. For example, for {@code TYPE_RGB}, * index 0 corresponds to red, index 1 to green, and index 2 to blue. *
* The translation from pixel sample values to color/alpha components for * display or processing purposes is based on a one-to-one correspondence of * samples to components. * Depending on the transfer type used to create an instance of * {@code ComponentColorModel}, the pixel sample values * represented by that instance may be signed or unsigned and may * be of integral type or float or double (see below for details). * The translation from sample values to normalized color/alpha components * must follow certain rules. For float and double samples, the translation * is an identity, i.e. normalized component values are equal to the * corresponding sample values. For integral samples, the translation * should be only a simple scale and offset, where the scale and offset * constants may be different for each component. The result of * applying the scale and offset constants is a set of color/alpha * component values, which are guaranteed to fall within a certain * range. Typically, the range for a color component will be the range * defined by the {@code getMinValue} and {@code getMaxValue} * methods of the {@code ColorSpace} class. The range for an * alpha component should be 0.0 to 1.0. *
* Instances of {@code ComponentColorModel} created with transfer types * {@code DataBuffer.TYPE_BYTE}, {@code DataBuffer.TYPE_USHORT}, * and {@code DataBuffer.TYPE_INT} have pixel sample values which * are treated as unsigned integral values. * The number of bits in a color or alpha sample of a pixel value might not * be the same as the number of bits for the corresponding color or alpha * sample passed to the * {@code ComponentColorModel(ColorSpace, int[], boolean, boolean, int, int)} * constructor. In * that case, this class assumes that the least significant n bits of a sample * value hold the component value, where n is the number of significant bits * for the component passed to the constructor. It also assumes that * any higher-order bits in a sample value are zero. Thus, sample values * range from 0 to 2n - 1. This class maps these sample values * to normalized color component values such that 0 maps to the value * obtained from the {@code ColorSpace's getMinValue} * method for each component and 2n - 1 maps to the value * obtained from {@code getMaxValue}. To create a * {@code ComponentColorModel} with a different color sample mapping * requires subclassing this class and overriding the * {@code getNormalizedComponents(Object, float[], int)} method. * The mapping for an alpha sample always maps 0 to 0.0 and * 2n - 1 to 1.0. *
* For instances with unsigned sample values, * the unnormalized color/alpha component representation is only * supported if two conditions hold. First, sample value 0 must * map to normalized component value 0.0 and sample value 2n - 1 * to 1.0. Second the min/max range of all color components of the * {@code ColorSpace} must be 0.0 to 1.0. In this case, the * component representation is the n least * significant bits of the corresponding sample. Thus each component is * an unsigned integral value between 0 and 2n - 1, where * n is the number of significant bits for a particular component. * If these conditions are not met, any method taking an unnormalized * component argument will throw an {@code IllegalArgumentException}. *
* Instances of {@code ComponentColorModel} created with transfer types * {@code DataBuffer.TYPE_SHORT}, {@code DataBuffer.TYPE_FLOAT}, and * {@code DataBuffer.TYPE_DOUBLE} have pixel sample values which * are treated as signed short, float, or double values. * Such instances do not support the unnormalized color/alpha component * representation, so any methods taking such a representation as an argument * will throw an {@code IllegalArgumentException} when called on one * of these instances. The normalized component values of instances * of this class have a range which depends on the transfer * type as follows: for float samples, the full range of the float data * type; for double samples, the full range of the float data type * (resulting from casting double to float); for short samples, * from approximately -maxVal to +maxVal, where maxVal is the per * component maximum value for the {@code ColorSpace} * (-32767 maps to -maxVal, 0 maps to 0.0, and 32767 maps * to +maxVal). A subclass may override the scaling for short sample * values to normalized component values by overriding the * {@code getNormalizedComponents(Object, float[], int)} method. * For float and double samples, the normalized component values are * taken to be equal to the corresponding sample values, and subclasses * should not attempt to add any non-identity scaling for these transfer * types. *
* Instances of {@code ComponentColorModel} created with transfer types * {@code DataBuffer.TYPE_SHORT}, {@code DataBuffer.TYPE_FLOAT}, and * {@code DataBuffer.TYPE_DOUBLE} * use all the bits of all sample values. Thus all color/alpha components * have 16 bits when using {@code DataBuffer.TYPE_SHORT}, 32 bits when * using {@code DataBuffer.TYPE_FLOAT}, and 64 bits when using * {@code DataBuffer.TYPE_DOUBLE}. When the * {@code ComponentColorModel(ColorSpace, int[], boolean, boolean, int, int)} * form of constructor is used with one of these transfer types, the * bits array argument is ignored. *
* It is possible to have color/alpha sample values * which cannot be reasonably interpreted as component values for rendering. * This can happen when {@code ComponentColorModel} is subclassed to * override the mapping of unsigned sample values to normalized color * component values or when signed sample values outside a certain range * are used. (As an example, specifying an alpha component as a signed * short value outside the range 0 to 32767, normalized range 0.0 to 1.0, can * lead to unexpected results.) It is the * responsibility of applications to appropriately scale pixel data before * rendering such that color components fall within the normalized range * of the {@code ColorSpace} (obtained using the {@code getMinValue} * and {@code getMaxValue} methods of the {@code ColorSpace} class) * and the alpha component is between 0.0 and 1.0. If color or alpha * component values fall outside these ranges, rendering results are * indeterminate. *
* Methods that use a single int pixel representation throw * an {@code IllegalArgumentException}, unless the number of components * for the {@code ComponentColorModel} is one and the component * value is unsigned -- in other words, a single color component using * a transfer type of {@code DataBuffer.TYPE_BYTE}, * {@code DataBuffer.TYPE_USHORT}, or {@code DataBuffer.TYPE_INT} * and no alpha. *
* A {@code ComponentColorModel} can be used in conjunction with a
* {@code ComponentSampleModel}, a {@code BandedSampleModel},
* or a {@code PixelInterleavedSampleModel} to construct a
* {@code BufferedImage}.
*
* @see ColorModel
* @see ColorSpace
* @see ComponentSampleModel
* @see BandedSampleModel
* @see PixelInterleavedSampleModel
* @see BufferedImage
*
*/
public class ComponentColorModel extends ColorModel {
/**
* {@code signed} is {@code true} for {@code short},
* {@code float}, and {@code double} transfer types; it
* is {@code false} for {@code byte}, {@code ushort},
* and {@code int} transfer types.
*/
private boolean signed; // true for transfer types short, float, double
// false for byte, ushort, int
private boolean is_sRGB_stdScale;
private boolean is_LinearRGB_stdScale;
private boolean is_LinearGray_stdScale;
private boolean is_ICCGray_stdScale;
private byte[] tosRGB8LUT;
private byte[] fromsRGB8LUT8;
private short[] fromsRGB8LUT16;
private byte[] fromLinearGray16ToOtherGray8LUT;
private short[] fromLinearGray16ToOtherGray16LUT;
private boolean needScaleInit;
private boolean noUnnorm;
private boolean nonStdScale;
private float[] min;
private float[] diffMinMax;
private float[] compOffset;
private float[] compScale;
/**
* Constructs a {@code ComponentColorModel} from the specified
* parameters. Color components will be in the specified
* {@code ColorSpace}. The supported transfer types are
* {@code DataBuffer.TYPE_BYTE}, {@code DataBuffer.TYPE_USHORT},
* {@code DataBuffer.TYPE_INT},
* {@code DataBuffer.TYPE_SHORT}, {@code DataBuffer.TYPE_FLOAT},
* and {@code DataBuffer.TYPE_DOUBLE}.
* If not null, the {@code bits} array specifies the
* number of significant bits per color and alpha component and its
* length should be at least the number of components in the
* {@code ColorSpace} if there is no alpha
* information in the pixel values, or one more than this number if
* there is alpha information. When the {@code transferType} is
* {@code DataBuffer.TYPE_SHORT}, {@code DataBuffer.TYPE_FLOAT},
* or {@code DataBuffer.TYPE_DOUBLE} the {@code bits} array
* argument is ignored. {@code hasAlpha} indicates whether alpha
* information is present. If {@code hasAlpha} is true, then
* the boolean {@code isAlphaPremultiplied}
* specifies how to interpret color and alpha samples in pixel values.
* If the boolean is true, color samples are assumed to have been
* multiplied by the alpha sample. The {@code transparency}
* specifies what alpha values can be represented by this color model.
* The acceptable {@code transparency} values are
* {@code OPAQUE}, {@code BITMASK} or {@code TRANSLUCENT}.
* The {@code transferType} is the type of primitive array used
* to represent pixel values.
*
* @param colorSpace The {@code ColorSpace} associated
* with this color model.
* @param bits The number of significant bits per component.
* May be null, in which case all bits of all
* component samples will be significant.
* Ignored if transferType is one of
* {@code DataBuffer.TYPE_SHORT},
* {@code DataBuffer.TYPE_FLOAT}, or
* {@code DataBuffer.TYPE_DOUBLE},
* in which case all bits of all component
* samples will be significant.
* @param hasAlpha If true, this color model supports alpha.
* @param isAlphaPremultiplied If true, alpha is premultiplied.
* @param transparency Specifies what alpha values can be represented
* by this color model.
* @param transferType Specifies the type of primitive array used to
* represent pixel values.
*
* @throws IllegalArgumentException If the {@code bits} array
* argument is not null, its length is less than the number of
* color and alpha components, and transferType is one of
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT}, or
* {@code DataBuffer.TYPE_INT}.
* @throws IllegalArgumentException If transferType is not one of
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT},
* {@code DataBuffer.TYPE_INT},
* {@code DataBuffer.TYPE_SHORT},
* {@code DataBuffer.TYPE_FLOAT}, or
* {@code DataBuffer.TYPE_DOUBLE}.
*
* @see ColorSpace
* @see java.awt.Transparency
*/
public ComponentColorModel (ColorSpace colorSpace,
int[] bits,
boolean hasAlpha,
boolean isAlphaPremultiplied,
int transparency,
int transferType) {
super (bitsHelper(transferType, colorSpace, hasAlpha),
bitsArrayHelper(bits, transferType, colorSpace, hasAlpha),
colorSpace, hasAlpha, isAlphaPremultiplied, transparency,
transferType);
switch(transferType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_INT:
signed = false;
needScaleInit = true;
break;
case DataBuffer.TYPE_SHORT:
signed = true;
needScaleInit = true;
break;
case DataBuffer.TYPE_FLOAT:
case DataBuffer.TYPE_DOUBLE:
signed = true;
needScaleInit = false;
noUnnorm = true;
nonStdScale = false;
break;
default:
throw new IllegalArgumentException("This constructor is not "+
"compatible with transferType " + transferType);
}
setupLUTs();
}
/**
* Constructs a {@code ComponentColorModel} from the specified
* parameters. Color components will be in the specified
* {@code ColorSpace}. The supported transfer types are
* {@code DataBuffer.TYPE_BYTE}, {@code DataBuffer.TYPE_USHORT},
* {@code DataBuffer.TYPE_INT},
* {@code DataBuffer.TYPE_SHORT}, {@code DataBuffer.TYPE_FLOAT},
* and {@code DataBuffer.TYPE_DOUBLE}. The number of significant
* bits per color and alpha component will be 8, 16, 32, 16, 32, or 64,
* respectively. The number of color components will be the
* number of components in the {@code ColorSpace}. There will be
* an alpha component if {@code hasAlpha} is {@code true}.
* If {@code hasAlpha} is true, then
* the boolean {@code isAlphaPremultiplied}
* specifies how to interpret color and alpha samples in pixel values.
* If the boolean is true, color samples are assumed to have been
* multiplied by the alpha sample. The {@code transparency}
* specifies what alpha values can be represented by this color model.
* The acceptable {@code transparency} values are
* {@code OPAQUE}, {@code BITMASK} or {@code TRANSLUCENT}.
* The {@code transferType} is the type of primitive array used
* to represent pixel values.
*
* @param colorSpace The {@code ColorSpace} associated
* with this color model.
* @param hasAlpha If true, this color model supports alpha.
* @param isAlphaPremultiplied If true, alpha is premultiplied.
* @param transparency Specifies what alpha values can be represented
* by this color model.
* @param transferType Specifies the type of primitive array used to
* represent pixel values.
*
* @throws IllegalArgumentException If transferType is not one of
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT},
* {@code DataBuffer.TYPE_INT},
* {@code DataBuffer.TYPE_SHORT},
* {@code DataBuffer.TYPE_FLOAT}, or
* {@code DataBuffer.TYPE_DOUBLE}.
*
* @see ColorSpace
* @see java.awt.Transparency
* @since 1.4
*/
public ComponentColorModel (ColorSpace colorSpace,
boolean hasAlpha,
boolean isAlphaPremultiplied,
int transparency,
int transferType) {
this(colorSpace, null, hasAlpha, isAlphaPremultiplied,
transparency, transferType);
}
private static int bitsHelper(int transferType,
ColorSpace colorSpace,
boolean hasAlpha) {
int numBits = DataBuffer.getDataTypeSize(transferType);
int numComponents = colorSpace.getNumComponents();
if (hasAlpha) {
++numComponents;
}
return numBits * numComponents;
}
private static int[] bitsArrayHelper(int[] origBits,
int transferType,
ColorSpace colorSpace,
boolean hasAlpha) {
switch(transferType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_INT:
if (origBits != null) {
return origBits;
}
break;
default:
break;
}
int numBits = DataBuffer.getDataTypeSize(transferType);
int numComponents = colorSpace.getNumComponents();
if (hasAlpha) {
++numComponents;
}
int[] bits = new int[numComponents];
for (int i = 0; i < numComponents; i++) {
bits[i] = numBits;
}
return bits;
}
private void setupLUTs() {
// REMIND: there is potential to accelerate sRGB, LinearRGB,
// LinearGray, ICCGray, and non-ICC Gray spaces with non-standard
// scaling, if that becomes important
//
// NOTE: The is_xxx_stdScale and nonStdScale booleans are provisionally
// set here when this method is called at construction time. These
// variables may be set again when initScale is called later.
// When setupLUTs returns, nonStdScale is true if (the transferType
// is not float or double) AND (some minimum ColorSpace component
// value is not 0.0 OR some maximum ColorSpace component value
// is not 1.0). This is correct for the calls to
// getNormalizedComponents(Object, float[], int) from initScale().
// initScale() may change the value nonStdScale based on the
// return value of getNormalizedComponents() - this will only
// happen if getNormalizedComponents() has been overridden by a
// subclass to make the mapping of min/max pixel sample values
// something different from min/max color component values.
if (is_sRGB) {
is_sRGB_stdScale = true;
nonStdScale = false;
} else if (ColorModel.isLinearRGBspace(colorSpace)) {
// Note that the built-in Linear RGB space has a normalized
// range of 0.0 - 1.0 for each coordinate. Usage of these
// LUTs makes that assumption.
is_LinearRGB_stdScale = true;
nonStdScale = false;
if (transferType == DataBuffer.TYPE_BYTE) {
tosRGB8LUT = ColorModel.getLinearRGB8TosRGB8LUT();
fromsRGB8LUT8 = ColorModel.getsRGB8ToLinearRGB8LUT();
} else {
tosRGB8LUT = ColorModel.getLinearRGB16TosRGB8LUT();
fromsRGB8LUT16 = ColorModel.getsRGB8ToLinearRGB16LUT();
}
} else if ((colorSpaceType == ColorSpace.TYPE_GRAY) &&
(colorSpace instanceof ICC_ColorSpace) &&
(colorSpace.getMinValue(0) == 0.0f) &&
(colorSpace.getMaxValue(0) == 1.0f)) {
// Note that a normalized range of 0.0 - 1.0 for the gray
// component is required, because usage of these LUTs makes
// that assumption.
ICC_ColorSpace ics = (ICC_ColorSpace) colorSpace;
is_ICCGray_stdScale = true;
nonStdScale = false;
fromsRGB8LUT16 = ColorModel.getsRGB8ToLinearRGB16LUT();
if (ColorModel.isLinearGRAYspace(ics)) {
is_LinearGray_stdScale = true;
if (transferType == DataBuffer.TYPE_BYTE) {
tosRGB8LUT = ColorModel.getGray8TosRGB8LUT(ics);
} else {
tosRGB8LUT = ColorModel.getGray16TosRGB8LUT(ics);
}
} else {
if (transferType == DataBuffer.TYPE_BYTE) {
tosRGB8LUT = ColorModel.getGray8TosRGB8LUT(ics);
fromLinearGray16ToOtherGray8LUT =
ColorModel.getLinearGray16ToOtherGray8LUT(ics);
} else {
tosRGB8LUT = ColorModel.getGray16TosRGB8LUT(ics);
fromLinearGray16ToOtherGray16LUT =
ColorModel.getLinearGray16ToOtherGray16LUT(ics);
}
}
} else if (needScaleInit) {
// if transferType is byte, ushort, int, or short and we
// don't already know the ColorSpace has minVlaue == 0.0f and
// maxValue == 1.0f for all components, we need to check that
// now and setup the min[] and diffMinMax[] arrays if necessary.
nonStdScale = false;
for (int i = 0; i < numColorComponents; i++) {
if ((colorSpace.getMinValue(i) != 0.0f) ||
(colorSpace.getMaxValue(i) != 1.0f)) {
nonStdScale = true;
break;
}
}
if (nonStdScale) {
min = new float[numColorComponents];
diffMinMax = new float[numColorComponents];
for (int i = 0; i < numColorComponents; i++) {
min[i] = colorSpace.getMinValue(i);
diffMinMax[i] = colorSpace.getMaxValue(i) - min[i];
}
}
}
}
private void initScale() {
// This method is called the first time any method which uses
// pixel sample value to color component value scaling information
// is called if the transferType supports non-standard scaling
// as defined above (byte, ushort, int, and short), unless the
// method is getNormalizedComponents(Object, float[], int) (that
// method must be overridden to use non-standard scaling). This
// method also sets up the noUnnorm boolean variable for these
// transferTypes. After this method is called, the nonStdScale
// variable will be true if getNormalizedComponents() maps a
// sample value of 0 to anything other than 0.0f OR maps a
// sample value of 2^^n - 1 (2^^15 - 1 for short transferType)
// to anything other than 1.0f. Note that this can be independent
// of the colorSpace min/max component values, if the
// getNormalizedComponents() method has been overridden for some
// reason, e.g. to provide greater dynamic range in the sample
// values than in the color component values. Unfortunately,
// this method can't be called at construction time, since a
// subclass may still have uninitialized state that would cause
// getNormalizedComponents() to return an incorrect result.
needScaleInit = false; // only needs to called once
if (nonStdScale || signed) {
// The unnormalized form is only supported for unsigned
// transferTypes and when the ColorSpace min/max values
// are 0.0/1.0. When this method is called nonStdScale is
// true if the latter condition does not hold. In addition,
// the unnormalized form requires that the full range of
// the pixel sample values map to the full 0.0 - 1.0 range
// of color component values. That condition is checked
// later in this method.
noUnnorm = true;
} else {
noUnnorm = false;
}
float[] lowVal, highVal;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
{
byte[] bpixel = new byte[numComponents];
for (int i = 0; i < numColorComponents; i++) {
bpixel[i] = 0;
}
if (supportsAlpha) {
bpixel[numColorComponents] =
(byte) ((1 << nBits[numColorComponents]) - 1);
}
lowVal = getNormalizedComponents(bpixel, null, 0);
for (int i = 0; i < numColorComponents; i++) {
bpixel[i] = (byte) ((1 << nBits[i]) - 1);
}
highVal = getNormalizedComponents(bpixel, null, 0);
}
break;
case DataBuffer.TYPE_USHORT:
{
short[] uspixel = new short[numComponents];
for (int i = 0; i < numColorComponents; i++) {
uspixel[i] = 0;
}
if (supportsAlpha) {
uspixel[numColorComponents] =
(short) ((1 << nBits[numColorComponents]) - 1);
}
lowVal = getNormalizedComponents(uspixel, null, 0);
for (int i = 0; i < numColorComponents; i++) {
uspixel[i] = (short) ((1 << nBits[i]) - 1);
}
highVal = getNormalizedComponents(uspixel, null, 0);
}
break;
case DataBuffer.TYPE_INT:
{
int[] ipixel = new int[numComponents];
for (int i = 0; i < numColorComponents; i++) {
ipixel[i] = 0;
}
if (supportsAlpha) {
ipixel[numColorComponents] =
((1 << nBits[numColorComponents]) - 1);
}
lowVal = getNormalizedComponents(ipixel, null, 0);
for (int i = 0; i < numColorComponents; i++) {
ipixel[i] = ((1 << nBits[i]) - 1);
}
highVal = getNormalizedComponents(ipixel, null, 0);
}
break;
case DataBuffer.TYPE_SHORT:
{
short[] spixel = new short[numComponents];
for (int i = 0; i < numColorComponents; i++) {
spixel[i] = 0;
}
if (supportsAlpha) {
spixel[numColorComponents] = 32767;
}
lowVal = getNormalizedComponents(spixel, null, 0);
for (int i = 0; i < numColorComponents; i++) {
spixel[i] = 32767;
}
highVal = getNormalizedComponents(spixel, null, 0);
}
break;
default:
lowVal = highVal = null; // to keep the compiler from complaining
break;
}
nonStdScale = false;
for (int i = 0; i < numColorComponents; i++) {
if ((lowVal[i] != 0.0f) || (highVal[i] != 1.0f)) {
nonStdScale = true;
break;
}
}
if (nonStdScale) {
noUnnorm = true;
is_sRGB_stdScale = false;
is_LinearRGB_stdScale = false;
is_LinearGray_stdScale = false;
is_ICCGray_stdScale = false;
compOffset = new float[numColorComponents];
compScale = new float[numColorComponents];
for (int i = 0; i < numColorComponents; i++) {
compOffset[i] = lowVal[i];
compScale[i] = 1.0f / (highVal[i] - lowVal[i]);
}
}
}
private int getRGBComponent(int pixel, int idx) {
if (numComponents > 1) {
throw new
IllegalArgumentException("More than one component per pixel");
}
if (signed) {
throw new
IllegalArgumentException("Component value is signed");
}
if (needScaleInit) {
initScale();
}
// Since there is only 1 component, there is no alpha
// Normalize the pixel in order to convert it
Object opixel = null;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
{
byte[] bpixel = { (byte) pixel };
opixel = bpixel;
}
break;
case DataBuffer.TYPE_USHORT:
{
short[] spixel = { (short) pixel };
opixel = spixel;
}
break;
case DataBuffer.TYPE_INT:
{
int[] ipixel = { pixel };
opixel = ipixel;
}
break;
}
float[] norm = getNormalizedComponents(opixel, null, 0);
float[] rgb = colorSpace.toRGB(norm);
return (int) (rgb[idx] * 255.0f + 0.5f);
}
/**
* Returns the red color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. A color conversion
* is done if necessary. The pixel value is specified as an int.
* The returned value will be a non pre-multiplied value.
* If the alpha is premultiplied, this method divides
* it out before returning the value (if the alpha value is 0,
* the red value will be 0).
*
* @param pixel The pixel from which you want to get the red color component.
*
* @return The red color component for the specified pixel, as an int.
*
* @throws IllegalArgumentException If there is more than
* one component in this {@code ColorModel}.
* @throws IllegalArgumentException If the component value for this
* {@code ColorModel} is signed
*/
public int getRed(int pixel) {
return getRGBComponent(pixel, 0);
}
/**
* Returns the green color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. A color conversion
* is done if necessary. The pixel value is specified as an int.
* The returned value will be a non
* pre-multiplied value. If the alpha is premultiplied, this method
* divides it out before returning the value (if the alpha value is 0,
* the green value will be 0).
*
* @param pixel The pixel from which you want to get the green color component.
*
* @return The green color component for the specified pixel, as an int.
*
* @throws IllegalArgumentException If there is more than
* one component in this {@code ColorModel}.
* @throws IllegalArgumentException If the component value for this
* {@code ColorModel} is signed
*/
public int getGreen(int pixel) {
return getRGBComponent(pixel, 1);
}
/**
* Returns the blue color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. A color conversion
* is done if necessary. The pixel value is specified as an int.
* The returned value will be a non
* pre-multiplied value. If the alpha is premultiplied, this method
* divides it out before returning the value (if the alpha value is 0,
* the blue value will be 0).
*
* @param pixel The pixel from which you want to get the blue color component.
*
* @return The blue color component for the specified pixel, as an int.
*
* @throws IllegalArgumentException If there is more than
* one component in this {@code ColorModel}.
* @throws IllegalArgumentException If the component value for this
* {@code ColorModel} is signed
*/
public int getBlue(int pixel) {
return getRGBComponent(pixel, 2);
}
/**
* Returns the alpha component for the specified pixel, scaled
* from 0 to 255. The pixel value is specified as an int.
*
* @param pixel The pixel from which you want to get the alpha component.
*
* @return The alpha component for the specified pixel, as an int.
*
* @throws IllegalArgumentException If there is more than
* one component in this {@code ColorModel}.
* @throws IllegalArgumentException If the component value for this
* {@code ColorModel} is signed
*/
public int getAlpha(int pixel) {
if (supportsAlpha == false) {
return 255;
}
if (numComponents > 1) {
throw new
IllegalArgumentException("More than one component per pixel");
}
if (signed) {
throw new
IllegalArgumentException("Component value is signed");
}
return (int) ((((float) pixel) / ((1<
* This method must be overridden by a subclass if that subclass
* is designed to translate pixel sample values to color component values
* in a non-default way. The default translations implemented by this
* class is described in the class comments. Any subclass implementing
* a non-default translation must follow the constraints on allowable
* translations defined there.
* @param pixel the specified pixel
* @param normComponents an array to receive the normalized components
* @param normOffset the offset into the {@code normComponents}
* array at which to start storing normalized components
* @return an array containing normalized color and alpha
* components.
* @throws ClassCastException if {@code pixel} is not a primitive
* array of type transferType
* @throws ArrayIndexOutOfBoundsException if
* {@code normComponents} is not large enough to hold all
* color and alpha components starting at {@code normOffset}
* @throws ArrayIndexOutOfBoundsException if
* {@code pixel} is not large enough to hold a pixel
* value for this {@code ColorModel}.
* @since 1.4
*/
public float[] getNormalizedComponents(Object pixel,
float[] normComponents,
int normOffset) {
if (normComponents == null) {
normComponents = new float[numComponents+normOffset];
}
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte[] bpixel = (byte[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) (bpixel[c] & 0xff)) /
((float) ((1 << nBits[c]) - 1));
}
break;
case DataBuffer.TYPE_USHORT:
short[] uspixel = (short[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) (uspixel[c] & 0xffff)) /
((float) ((1 << nBits[c]) - 1));
}
break;
case DataBuffer.TYPE_INT:
int[] ipixel = (int[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) ipixel[c]) /
((float) ((1 << nBits[c]) - 1));
}
break;
case DataBuffer.TYPE_SHORT:
short[] spixel = (short[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) spixel[c]) / 32767.0f;
}
break;
case DataBuffer.TYPE_FLOAT:
float[] fpixel = (float[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = fpixel[c];
}
break;
case DataBuffer.TYPE_DOUBLE:
double[] dpixel = (double[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = (float) dpixel[c];
}
break;
default:
throw new UnsupportedOperationException("This method has not been "+
"implemented for transferType " +
transferType);
}
if (supportsAlpha && isAlphaPremultiplied) {
float alpha = normComponents[numColorComponents + normOffset];
if (alpha != 0.0f) {
float invAlpha = 1.0f / alpha;
for (int c = normOffset; c < numColorComponents + normOffset;
c++) {
normComponents[c] *= invAlpha;
}
}
}
if (min != null) {
// Normally (i.e. when this class is not subclassed to override
// this method), the test (min != null) will be equivalent to
// the test (nonStdScale). However, there is an unlikely, but
// possible case, in which this method is overridden, nonStdScale
// is set true by initScale(), the subclass method for some
// reason calls this superclass method, but the min and
// diffMinMax arrays were never initialized by setupLUTs(). In
// that case, the right thing to do is follow the intended
// semantics of this method, and rescale the color components
// only if the ColorSpace min/max were detected to be other
// than 0.0/1.0 by setupLUTs(). Note that this implies the
// transferType is byte, ushort, int, or short - i.e. components
// derived from float and double pixel data are never rescaled.
for (int c = 0; c < numColorComponents; c++) {
normComponents[c + normOffset] = min[c] +
diffMinMax[c] * normComponents[c + normOffset];
}
}
return normComponents;
}
/**
* Forces the raster data to match the state specified in the
* {@code isAlphaPremultiplied} variable, assuming the data
* is currently correctly described by this {@code ColorModel}.
* It may multiply or divide the color raster data by alpha, or
* do nothing if the data is in the correct state. If the data needs
* to be coerced, this method also returns an instance of
* this {@code ColorModel} with
* the {@code isAlphaPremultiplied} flag set appropriately.
* Since {@code ColorModel} can be subclassed, subclasses inherit
* the implementation of this method and if they don't override it
* then they throw an exception if they use an unsupported
* {@code transferType}.
*
* @throws NullPointerException if {@code raster} is
* {@code null} and data coercion is required.
* @throws UnsupportedOperationException if the transfer type of
* this {@code ComponentColorModel}
* is not one of the supported transfer types:
* {@code DataBuffer.TYPE_BYTE}, {@code DataBuffer.TYPE_USHORT},
* {@code DataBuffer.TYPE_INT}, {@code DataBuffer.TYPE_SHORT},
* {@code DataBuffer.TYPE_FLOAT}, or {@code DataBuffer.TYPE_DOUBLE}.
*/
public ColorModel coerceData (WritableRaster raster,
boolean isAlphaPremultiplied) {
if ((supportsAlpha == false) ||
(this.isAlphaPremultiplied == isAlphaPremultiplied))
{
// Nothing to do
return this;
}
int w = raster.getWidth();
int h = raster.getHeight();
int aIdx = raster.getNumBands() - 1;
float normAlpha;
int rminX = raster.getMinX();
int rY = raster.getMinY();
int rX;
if (isAlphaPremultiplied) {
switch (transferType) {
case DataBuffer.TYPE_BYTE: {
byte pixel[] = null;
byte zpixel[] = null;
float alphaScale = 1.0f / ((float) ((1<