blob: 0a1f8749da678edd57ea0bfcf2d890d87f68fc8d [file] [log] [blame]
/*
* Copyright (c) 1995, 2021, 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.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import sun.java2d.cmm.CMSManager;
import sun.java2d.cmm.ColorTransform;
import sun.java2d.cmm.PCMM;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Arrays;
/**
* The {@code ColorModel} abstract class encapsulates the
* methods for translating a pixel value to color components
* (for example, red, green, and blue) and an alpha component.
* In order to render an image to the screen, a printer, or another
* image, pixel values must be converted to color and alpha components.
* As arguments to or return values from methods of this class,
* pixels are represented as 32-bit ints or as arrays of primitive types.
* The number, order, and interpretation of color components for a
* {@code ColorModel} is specified by its {@code ColorSpace}.
* A {@code ColorModel} used with pixel data that does not include
* alpha information treats all pixels as opaque, which is an alpha
* value of 1.0.
* <p>
* This {@code ColorModel} class supports two representations of
* pixel values. A pixel value can be a single 32-bit int or an
* array of primitive types. The Java(tm) Platform 1.0 and 1.1 APIs
* represented pixels as single {@code byte} or single
* {@code int} values. For purposes of the {@code ColorModel}
* class, pixel value arguments were passed as ints. The Java(tm) 2
* Platform API introduced additional classes for representing images.
* With {@link BufferedImage} or {@link RenderedImage}
* objects, based on {@link Raster} and {@link SampleModel} classes, pixel
* values might not be conveniently representable as a single int.
* Consequently, {@code ColorModel} now has methods that accept
* pixel values represented as arrays of primitive types. The primitive
* type used by a particular {@code ColorModel} object is called its
* transfer type.
* <p>
* {@code ColorModel} objects used with images for which pixel values
* are not conveniently representable as a single int throw an
* {@link IllegalArgumentException} when methods taking a single int pixel
* argument are called. Subclasses of {@code ColorModel} must
* specify the conditions under which this occurs. This does not
* occur with {@link DirectColorModel} or {@link IndexColorModel} objects.
* <p>
* Currently, the transfer types supported by the Java 2D(tm) API are
* DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT,
* DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, and DataBuffer.TYPE_DOUBLE.
* Most rendering operations will perform much faster when using ColorModels
* and images based on the first three of these types. In addition, some
* image filtering operations are not supported for ColorModels and
* images based on the latter three types.
* The transfer type for a particular {@code ColorModel} object is
* specified when the object is created, either explicitly or by default.
* All subclasses of {@code ColorModel} must specify what the
* possible transfer types are and how the number of elements in the
* primitive arrays representing pixels is determined.
* <p>
* For {@code BufferedImages}, the transfer type of its
* {@code Raster} and of the {@code Raster} object's
* {@code SampleModel} (available from the
* {@code getTransferType} methods of these classes) must match that
* of the {@code ColorModel}. The number of elements in an array
* representing a pixel for the {@code Raster} and
* {@code SampleModel} (available from the
* {@code getNumDataElements} methods of these classes) must match
* that of the {@code ColorModel}.
* <p>
* The algorithm used to convert from pixel values to color and alpha
* components varies by subclass. For example, there is not necessarily
* a one-to-one correspondence between samples obtained from the
* {@code SampleModel} of a {@code BufferedImage} object's
* {@code Raster} and color/alpha components. Even when
* there is such a correspondence, the number of bits in a sample is not
* necessarily the same as the number of bits in the corresponding color/alpha
* component. Each subclass must specify how the translation from
* pixel values to color/alpha components is done.
* <p>
* Methods in the {@code ColorModel} class use two different
* representations of color and alpha components - a normalized form
* and an unnormalized form. In the normalized form, each component is a
* {@code float} value between some minimum and maximum values. For
* the alpha component, the minimum is 0.0 and the maximum is 1.0. For
* color components the minimum and maximum values for each component can
* be obtained from the {@code ColorSpace} object. These values
* will often be 0.0 and 1.0 (e.g. normalized component values for the
* default sRGB color space range from 0.0 to 1.0), but some color spaces
* have component values with different upper and lower limits. These
* limits can be obtained using the {@code getMinValue} and
* {@code getMaxValue} methods of the {@code ColorSpace}
* class. Normalized color component values are not premultiplied.
* All {@code ColorModels} must support the normalized form.
* <p>
* In the unnormalized
* form, each component is an unsigned integral value between 0 and
* 2<sup>n</sup> - 1, where n is the number of significant bits for a
* particular component. If pixel values for a particular
* {@code ColorModel} represent color samples premultiplied by
* the alpha sample, unnormalized color component values are
* also premultiplied. The unnormalized form is used only with instances
* of {@code ColorModel} whose {@code ColorSpace} has minimum
* component values of 0.0 for all components and maximum values of
* 1.0 for all components.
* The unnormalized form for color and alpha components can be a convenient
* representation for {@code ColorModels} whose normalized component
* values all lie
* between 0.0 and 1.0. In such cases the integral value 0 maps to 0.0 and
* the value 2<sup>n</sup> - 1 maps to 1.0. In other cases, such as
* when the normalized component values can be either negative or positive,
* the unnormalized form is not convenient. Such {@code ColorModel}
* objects throw an {@link IllegalArgumentException} when methods involving
* an unnormalized argument are called. Subclasses of {@code ColorModel}
* must specify the conditions under which this occurs.
*
* @see IndexColorModel
* @see ComponentColorModel
* @see PackedColorModel
* @see DirectColorModel
* @see java.awt.Image
* @see BufferedImage
* @see RenderedImage
* @see java.awt.color.ColorSpace
* @see SampleModel
* @see Raster
* @see DataBuffer
*/
public abstract class ColorModel implements Transparency{
private long pData; // Placeholder for data for native functions
/**
* The total number of bits in the pixel.
*/
protected int pixel_bits;
int[] nBits;
int transparency = Transparency.TRANSLUCENT;
boolean supportsAlpha = true;
boolean isAlphaPremultiplied = false;
int numComponents = -1;
int numColorComponents = -1;
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int colorSpaceType = ColorSpace.TYPE_RGB;
int maxBits;
boolean is_sRGB = true;
/**
* Data type of the array used to represent pixel values.
*/
protected int transferType;
/**
* This is copied from java.awt.Toolkit since we need the library
* loaded in java.awt.image also:
*
* WARNING: This is a temporary workaround for a problem in the
* way the AWT loads native libraries. A number of classes in the
* AWT package have a native method, initIDs(), which initializes
* the JNI field and method ids used in the native portion of
* their implementation.
*
* Since the use and storage of these ids is done by the
* implementation libraries, the implementation of these method is
* provided by the particular AWT implementations (for example,
* "Toolkit"s/Peer), such as Motif, Microsoft Windows, or Tiny. The
* problem is that this means that the native libraries must be
* loaded by the java.* classes, which do not necessarily know the
* names of the libraries to load. A better way of doing this
* would be to provide a separate library which defines java.awt.*
* initIDs, and exports the relevant symbols out to the
* implementation libraries.
*
* For now, we know it's done by the implementation, and we assume
* that the name of the library is "awt". -br.
*/
private static boolean loaded = false;
@SuppressWarnings("removal")
static void loadLibraries() {
if (!loaded) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("awt");
return null;
}
});
loaded = true;
}
}
private static native void initIDs();
static {
/* ensure that the proper libraries are loaded */
loadLibraries();
initIDs();
}
private static ColorModel RGBdefault;
/**
* Returns a {@code DirectColorModel} that describes the default
* format for integer RGB values used in many of the methods in the
* AWT image interfaces for the convenience of the programmer.
* The color space is the default {@link ColorSpace}, sRGB.
* The format for the RGB values is an integer with 8 bits
* each of alpha, red, green, and blue color components ordered
* correspondingly from the most significant byte to the least
* significant byte, as in: 0xAARRGGBB. Color components are
* not premultiplied by the alpha component. This format does not
* necessarily represent the native or the most efficient
* {@code ColorModel} for a particular device or for all images.
* It is merely used as a common color model format.
* @return a {@code DirectColorModel} object describing default
* RGB values.
*/
public static ColorModel getRGBdefault() {
if (RGBdefault == null) {
RGBdefault = new DirectColorModel(32,
0x00ff0000, // Red
0x0000ff00, // Green
0x000000ff, // Blue
0xff000000 // Alpha
);
}
return RGBdefault;
}
/**
* Constructs a {@code ColorModel} that translates pixels of the
* specified number of bits to color/alpha components. The color
* space is the default RGB {@code ColorSpace}, which is sRGB.
* Pixel values are assumed to include alpha information. If color
* and alpha information are represented in the pixel value as
* separate spatial bands, the color bands are assumed not to be
* premultiplied with the alpha value. The transparency type is
* java.awt.Transparency.TRANSLUCENT. The transfer type will be the
* smallest of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
* or DataBuffer.TYPE_INT that can hold a single pixel
* (or DataBuffer.TYPE_UNDEFINED if bits is greater
* than 32). Since this constructor has no information about the
* number of bits per color and alpha component, any subclass calling
* this constructor should override any method that requires this
* information.
* @param bits the number of bits of a pixel
* @throws IllegalArgumentException if the number
* of bits in {@code bits} is less than 1
*/
public ColorModel(int bits) {
pixel_bits = bits;
if (bits < 1) {
throw new IllegalArgumentException("Number of bits must be > 0");
}
numComponents = 4;
numColorComponents = 3;
maxBits = bits;
// REMIND: make sure transferType is set correctly
transferType = ColorModel.getDefaultTransferType(bits);
}
/**
* Constructs a {@code ColorModel} that translates pixel values
* to color/alpha components. Color components will be in the
* specified {@code ColorSpace}. {@code pixel_bits} is the
* number of bits in the pixel values. The bits array
* specifies the number of significant bits per color and alpha component.
* Its length should be 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. {@code hasAlpha} indicates whether or not alpha
* information is present. The {@code boolean}
* {@code isAlphaPremultiplied} specifies how to interpret pixel
* values in which color and alpha information are represented as
* separate spatial bands. If the {@code boolean}
* is {@code 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 transfer type is the type of primitive array used to represent
* pixel values. Note that the bits array contains the number of
* significant bits per color/alpha component after the translation
* from pixel values. For example, for an
* {@code IndexColorModel} with {@code pixel_bits} equal to
* 16, the bits array might have four elements with each element set
* to 8.
* @param pixel_bits the number of bits in the pixel values
* @param bits array that specifies the number of significant bits
* per color and alpha component
* @param cspace the specified {@code ColorSpace}
* @param hasAlpha {@code true} if alpha information is present;
* {@code false} otherwise
* @param isAlphaPremultiplied {@code true} if color samples are
* assumed to be premultiplied by the alpha samples;
* {@code false} otherwise
* @param transparency what alpha values can be represented by this
* color model
* @param transferType the type of the array used to represent pixel
* values
* @throws IllegalArgumentException if the length of
* the bit array is less than the number of color or alpha
* components in this {@code ColorModel}, or if the
* transparency is not a valid value.
* @throws IllegalArgumentException if the sum of the number
* of bits in {@code bits} is less than 1 or if
* any of the elements in {@code bits} is less than 0.
* @see java.awt.Transparency
*/
protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace,
boolean hasAlpha,
boolean isAlphaPremultiplied,
int transparency,
int transferType) {
colorSpace = cspace;
colorSpaceType = cspace.getType();
numColorComponents = cspace.getNumComponents();
numComponents = numColorComponents + (hasAlpha ? 1 : 0);
supportsAlpha = hasAlpha;
if (bits.length < numComponents) {
throw new IllegalArgumentException("Number of color/alpha "+
"components should be "+
numComponents+
" but length of bits array is "+
bits.length);
}
// 4186669
if (transparency < Transparency.OPAQUE ||
transparency > Transparency.TRANSLUCENT)
{
throw new IllegalArgumentException("Unknown transparency: "+
transparency);
}
if (supportsAlpha == false) {
this.isAlphaPremultiplied = false;
this.transparency = Transparency.OPAQUE;
}
else {
this.isAlphaPremultiplied = isAlphaPremultiplied;
this.transparency = transparency;
}
/*
* We need significant bits value only for the length
* of number of components, so we truncate remaining part.
* It also helps in hashCode calculation since bits[] can contain
* different values after the length of number of components between
* two ColorModels.
*/
nBits = Arrays.copyOf(bits, numComponents);
this.pixel_bits = pixel_bits;
if (pixel_bits <= 0) {
throw new IllegalArgumentException("Number of pixel bits must "+
"be > 0");
}
// Check for bits < 0
maxBits = 0;
for (int i=0; i < bits.length; i++) {
// bug 4304697
if (bits[i] < 0) {
throw new
IllegalArgumentException("Number of bits must be >= 0");
}
if (maxBits < bits[i]) {
maxBits = bits[i];
}
}
// Make sure that we don't have all 0-bit components
if (maxBits == 0) {
throw new IllegalArgumentException("There must be at least "+
"one component with > 0 "+
"pixel bits.");
}
// Save this since we always need to check if it is the default CS
if (cspace != ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
is_sRGB = false;
}
// Save the transfer type
this.transferType = transferType;
}
/**
* Returns whether or not alpha is supported in this
* {@code ColorModel}.
* @return {@code true} if alpha is supported in this
* {@code ColorModel}; {@code false} otherwise.
*/
public final boolean hasAlpha() {
return supportsAlpha;
}
/**
* Returns whether or not the alpha has been premultiplied in the
* pixel values to be translated by this {@code ColorModel}.
* If the boolean is {@code true}, this {@code ColorModel}
* is to be used to interpret pixel values in which color and alpha
* information are represented as separate spatial bands, and color
* samples are assumed to have been multiplied by the
* alpha sample.
* @return {@code true} if the alpha values are premultiplied
* in the pixel values to be translated by this
* {@code ColorModel}; {@code false} otherwise.
*/
public final boolean isAlphaPremultiplied() {
return isAlphaPremultiplied;
}
/**
* Returns the transfer type of this {@code ColorModel}.
* The transfer type is the type of primitive array used to represent
* pixel values as arrays.
* @return the transfer type.
* @since 1.3
*/
public final int getTransferType() {
return transferType;
}
/**
* Returns the number of bits per pixel described by this
* {@code ColorModel}.
* @return the number of bits per pixel.
*/
public int getPixelSize() {
return pixel_bits;
}
/**
* Returns the number of bits for the specified color/alpha component.
* Color components are indexed in the order specified by the
* {@code ColorSpace}. Typically, this order reflects the name
* of the color space type. For example, for TYPE_RGB, index 0
* corresponds to red, index 1 to green, and index 2
* to blue. If this {@code ColorModel} supports alpha, the alpha
* component corresponds to the index following the last color
* component.
* @param componentIdx the index of the color/alpha component
* @return the number of bits for the color/alpha component at the
* specified index.
* @throws ArrayIndexOutOfBoundsException if {@code componentIdx}
* is greater than the number of components or
* less than zero
* @throws NullPointerException if the number of bits array is
* {@code null}
*/
public int getComponentSize(int componentIdx) {
// REMIND:
if (nBits == null) {
throw new NullPointerException("Number of bits array is null.");
}
return nBits[componentIdx];
}
/**
* Returns an array of the number of bits per color/alpha component.
* The array contains the color components in the order specified by the
* {@code ColorSpace}, followed by the alpha component, if
* present.
* @return an array of the number of bits per color/alpha component
*/
public int[] getComponentSize() {
if (nBits != null) {
return nBits.clone();
}
return null;
}
/**
* Returns the transparency. Returns either OPAQUE, BITMASK,
* or TRANSLUCENT.
* @return the transparency of this {@code ColorModel}.
* @see Transparency#OPAQUE
* @see Transparency#BITMASK
* @see Transparency#TRANSLUCENT
*/
public int getTransparency() {
return transparency;
}
/**
* Returns the number of components, including alpha, in this
* {@code ColorModel}. This is equal to the number of color
* components, optionally plus one, if there is an alpha component.
* @return the number of components in this {@code ColorModel}
*/
public int getNumComponents() {
return numComponents;
}
/**
* Returns the number of color components in this
* {@code ColorModel}.
* This is the number of components returned by
* {@link ColorSpace#getNumComponents}.
* @return the number of color components in this
* {@code ColorModel}.
* @see ColorSpace#getNumComponents
*/
public int getNumColorComponents() {
return numColorComponents;
}
/**
* 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.
* An {@code IllegalArgumentException} is thrown if pixel
* values for this {@code ColorModel} are not conveniently
* representable as a single int. The returned value is not a
* pre-multiplied value. For example, if the
* alpha is premultiplied, this method divides it out before returning
* the value. If the alpha value is 0, the red value is 0.
* @param pixel a specified pixel
* @return the value of the red component of the specified pixel.
*/
public abstract int getRed(int pixel);
/**
* 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.
* An {@code IllegalArgumentException} is thrown if pixel
* values for this {@code ColorModel} are not conveniently
* representable as a single int. The returned value is a non
* pre-multiplied value. For example, if the alpha is premultiplied,
* this method divides it out before returning
* the value. If the alpha value is 0, the green value is 0.
* @param pixel the specified pixel
* @return the value of the green component of the specified pixel.
*/
public abstract int getGreen(int pixel);
/**
* 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.
* An {@code IllegalArgumentException} is thrown if pixel values
* for this {@code ColorModel} are not conveniently representable
* as a single int. The returned value is a non pre-multiplied
* value, for example, if the alpha is premultiplied, this method
* divides it out before returning the value. If the alpha value is
* 0, the blue value is 0.
* @param pixel the specified pixel
* @return the value of the blue component of the specified pixel.
*/
public abstract int getBlue(int pixel);
/**
* Returns the alpha component for the specified pixel, scaled
* from 0 to 255. The pixel value is specified as an int.
* An {@code IllegalArgumentException} is thrown if pixel
* values for this {@code ColorModel} are not conveniently
* representable as a single int.
* @param pixel the specified pixel
* @return the value of alpha component of the specified pixel.
*/
public abstract int getAlpha(int pixel);
/**
* Returns the color/alpha components of the pixel in the default
* RGB color model format. A color conversion is done if necessary.
* The pixel value is specified as an int.
* An {@code IllegalArgumentException} thrown if pixel values
* for this {@code ColorModel} are not conveniently representable
* as a single int. The returned value is in a non
* pre-multiplied format. For example, if the alpha is premultiplied,
* this method divides it out of the color components. If the alpha
* value is 0, the color values are 0.
* @param pixel the specified pixel
* @return the RGB value of the color/alpha components of the
* specified pixel.
* @see ColorModel#getRGBdefault
*/
public int getRGB(int pixel) {
return (getAlpha(pixel) << 24)
| (getRed(pixel) << 16)
| (getGreen(pixel) << 8)
| (getBlue(pixel) << 0);
}
/**
* Returns the red color component for the specified pixel, scaled
* from 0 to 255 in the default RGB {@code ColorSpace}, sRGB. A
* color conversion is done if necessary. The pixel value is
* specified by an array of data elements of type transferType passed
* in as an object reference. The returned value is a non
* pre-multiplied value. For example, if alpha is premultiplied,
* this method divides it out before returning
* the value. If the alpha value is 0, the red value is 0.
* If {@code inData} is not a primitive array of type
* transferType, a {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code inData} is not large enough to hold a pixel value for
* this {@code ColorModel}.
* If this {@code transferType} is not supported, a
* {@code UnsupportedOperationException} will be
* thrown. Since
* {@code ColorModel} is an abstract class, any instance
* must be an instance of a subclass. Subclasses inherit the
* implementation of this method and if they don't override it, this
* method throws an exception if the subclass uses a
* {@code transferType} other than
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT}, or
* {@code DataBuffer.TYPE_INT}.
* @param inData an array of pixel values
* @return the value of the red component of the specified pixel.
* @throws ClassCastException if {@code inData}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code inData} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
* {@code transferType} is not supported by this
* {@code ColorModel}
*/
public int getRed(Object inData) {
int pixel=0,length=0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short[] sdata = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
break;
case DataBuffer.TYPE_INT:
int[] idata = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException("This method has not been "+
"implemented for transferType " + transferType);
}
if (length == 1) {
return getRed(pixel);
}
else {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
}
/**
* Returns the green color component for the specified pixel, scaled
* from 0 to 255 in the default RGB {@code ColorSpace}, sRGB. A
* color conversion is done if necessary. The pixel value is
* specified by an array of data elements of type transferType passed
* in as an object reference. The returned value will be a non
* pre-multiplied value. For example, if the alpha is premultiplied,
* this method divides it out before returning the value. If the
* alpha value is 0, the green value is 0. If {@code inData} is
* not a primitive array of type transferType, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code inData} is not large enough to hold a pixel value for
* this {@code ColorModel}.
* If this {@code transferType} is not supported, a
* {@code UnsupportedOperationException} will be
* thrown. Since
* {@code ColorModel} is an abstract class, any instance
* must be an instance of a subclass. Subclasses inherit the
* implementation of this method and if they don't override it, this
* method throws an exception if the subclass uses a
* {@code transferType} other than
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT}, or
* {@code DataBuffer.TYPE_INT}.
* @param inData an array of pixel values
* @return the value of the green component of the specified pixel.
* @throws ClassCastException if {@code inData}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code inData} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
* {@code transferType} is not supported by this
* {@code ColorModel}
*/
public int getGreen(Object inData) {
int pixel=0,length=0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short[] sdata = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
break;
case DataBuffer.TYPE_INT:
int[] idata = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException("This method has not been "+
"implemented for transferType " + transferType);
}
if (length == 1) {
return getGreen(pixel);
}
else {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
}
/**
* Returns the blue color component for the specified pixel, scaled
* from 0 to 255 in the default RGB {@code ColorSpace}, sRGB. A
* color conversion is done if necessary. The pixel value is
* specified by an array of data elements of type transferType passed
* in as an object reference. The returned value is a non
* pre-multiplied value. For example, 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. If
* {@code inData} is not a primitive array of type transferType,
* a {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is
* thrown if {@code inData} is not large enough to hold a pixel
* value for this {@code ColorModel}.
* If this {@code transferType} is not supported, a
* {@code UnsupportedOperationException} will be
* thrown. Since
* {@code ColorModel} is an abstract class, any instance
* must be an instance of a subclass. Subclasses inherit the
* implementation of this method and if they don't override it, this
* method throws an exception if the subclass uses a
* {@code transferType} other than
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT}, or
* {@code DataBuffer.TYPE_INT}.
* @param inData an array of pixel values
* @return the value of the blue component of the specified pixel.
* @throws ClassCastException if {@code inData}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code inData} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
* {@code transferType} is not supported by this
* {@code ColorModel}
*/
public int getBlue(Object inData) {
int pixel=0,length=0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short[] sdata = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
break;
case DataBuffer.TYPE_INT:
int[] idata = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException("This method has not been "+
"implemented for transferType " + transferType);
}
if (length == 1) {
return getBlue(pixel);
}
else {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
}
/**
* Returns the alpha component for the specified pixel, scaled
* from 0 to 255. The pixel value is specified by an array of data
* elements of type transferType passed in as an object reference.
* If inData is not a primitive array of type transferType, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code inData} is not large enough to hold a pixel value for
* this {@code ColorModel}.
* If this {@code transferType} is not supported, a
* {@code UnsupportedOperationException} will be
* thrown. Since
* {@code ColorModel} is an abstract class, any instance
* must be an instance of a subclass. Subclasses inherit the
* implementation of this method and if they don't override it, this
* method throws an exception if the subclass uses a
* {@code transferType} other than
* {@code DataBuffer.TYPE_BYTE},
* {@code DataBuffer.TYPE_USHORT}, or
* {@code DataBuffer.TYPE_INT}.
* @param inData the specified pixel
* @return the alpha component of the specified pixel, scaled from
* 0 to 255.
* @throws ClassCastException if {@code inData}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code inData} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
* {@code tranferType} is not supported by this
* {@code ColorModel}
*/
public int getAlpha(Object inData) {
int pixel=0,length=0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short[] sdata = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
break;
case DataBuffer.TYPE_INT:
int[] idata = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException("This method has not been "+
"implemented for transferType " + transferType);
}
if (length == 1) {
return getAlpha(pixel);
}
else {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
}
/**
* Returns the color/alpha components for the specified pixel in the
* default RGB color model format. A color conversion is done if
* necessary. The pixel value is specified by an array of data
* elements of type transferType passed in as an object reference.
* If inData is not a primitive array of type transferType, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is
* thrown if {@code inData} is not large enough to hold a pixel
* value for this {@code ColorModel}.
* The returned value will be in a non pre-multiplied format, i.e. if
* the alpha is premultiplied, this method will divide it out of the
* color components (if the alpha value is 0, the color values will be 0).
* @param inData the specified pixel
* @return the color and alpha components of the specified pixel.
* @see ColorModel#getRGBdefault
*/
public int getRGB(Object inData) {
return (getAlpha(inData) << 24)
| (getRed(inData) << 16)
| (getGreen(inData) << 8)
| (getBlue(inData) << 0);
}
/**
* Returns a data element array representation of a pixel in this
* {@code ColorModel}, given an integer pixel representation in
* the default RGB color model.
* This array can then be passed to the
* {@link WritableRaster#setDataElements} method of
* a {@link WritableRaster} object. If the pixel variable is
* {@code null}, a new array will be allocated. If
* {@code pixel} is not
* {@code null}, it must be a primitive array of type
* {@code transferType}; otherwise, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code pixel} is
* not large enough to hold a pixel value for this
* {@code ColorModel}. The pixel array is returned.
* If this {@code transferType} is not supported, a
* {@code UnsupportedOperationException} will be
* thrown. Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param rgb the integer pixel representation in the default RGB
* color model
* @param pixel the specified pixel
* @return an array representation of the specified pixel in this
* {@code ColorModel}.
* @throws ClassCastException if {@code pixel}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code pixel} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
* @see WritableRaster#setDataElements
* @see SampleModel#setDataElements
*/
public Object getDataElements(int rgb, Object pixel) {
throw new UnsupportedOperationException
("This method is not supported by this color model.");
}
/**
* Returns an array of unnormalized color/alpha components given a pixel
* in this {@code ColorModel}. The pixel value is specified as
* an {@code int}. An {@code IllegalArgumentException}
* will be thrown if pixel values for this {@code ColorModel} are
* not conveniently representable as a single {@code int} or if
* color component values for this {@code ColorModel} are not
* conveniently representable in the unnormalized form.
* For example, this method can be used to retrieve the
* components for a specific pixel value in a
* {@code DirectColorModel}. If the components array is
* {@code null}, a new array will be allocated. The
* components array will be returned. Color/alpha components are
* stored in the components array starting at {@code offset}
* (even if the array is allocated by this method). An
* {@code ArrayIndexOutOfBoundsException} is thrown if the
* components array is not {@code null} and is not large
* enough to hold all the color and alpha components (starting at offset).
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param pixel the specified pixel
* @param components the array to receive the color and alpha
* components of the specified pixel
* @param offset the offset into the {@code components} array at
* which to start storing the color and alpha components
* @return an array containing the color and alpha components of the
* specified pixel starting at the specified offset.
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
*/
public int[] getComponents(int pixel, int[] components, int offset) {
throw new UnsupportedOperationException
("This method is not supported by this color model.");
}
/**
* Returns an array of unnormalized color/alpha components given a pixel
* in this {@code ColorModel}. The pixel value is specified by
* an array of data elements of type transferType passed in as an
* object reference. If {@code pixel} is not a primitive array
* of type transferType, a {@code ClassCastException} is thrown.
* An {@code IllegalArgumentException} will be thrown if color
* component values for this {@code ColorModel} are not
* conveniently representable in the unnormalized form.
* An {@code ArrayIndexOutOfBoundsException} is
* thrown if {@code pixel} is not large enough to hold a pixel
* value for this {@code ColorModel}.
* This method can be used to retrieve the components for a specific
* pixel value in any {@code ColorModel}. If the components
* array is {@code null}, a new array will be allocated. The
* components array will be returned. Color/alpha components are
* stored in the {@code components} array starting at
* {@code offset} (even if the array is allocated by this
* method). An {@code ArrayIndexOutOfBoundsException}
* is thrown if the components array is not {@code null} and is
* not large enough to hold all the color and alpha components
* (starting at {@code offset}).
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param pixel the specified pixel
* @param components an array that receives the color and alpha
* components of the specified pixel
* @param offset the index into the {@code components} array at
* which to begin storing the color and alpha components of the
* specified pixel
* @return an array containing the color and alpha components of the
* specified pixel starting at the specified offset.
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
*/
public int[] getComponents(Object pixel, int[] components, int offset) {
throw new UnsupportedOperationException
("This method is not supported by this color model.");
}
/**
* Returns an array of all of the color/alpha components in unnormalized
* form, given a normalized component array. Unnormalized components
* are unsigned integral values between 0 and 2<sup>n</sup> - 1, where
* n is the number of bits for a particular component. Normalized
* components are float values between a per component minimum and
* maximum specified by the {@code ColorSpace} object for this
* {@code ColorModel}. An {@code IllegalArgumentException}
* will be thrown if color component values for this
* {@code ColorModel} are not conveniently representable in the
* unnormalized form. If the
* {@code components} array is {@code null}, a new array
* will be allocated. The {@code components} array will
* be returned. Color/alpha components are stored in the
* {@code components} array starting at {@code offset} (even
* if the array is allocated by this method). An
* {@code ArrayIndexOutOfBoundsException} is thrown if the
* {@code components} array is not {@code null} and is not
* large enough to hold all the color and alpha
* components (starting at {@code offset}). An
* {@code IllegalArgumentException} is thrown if the
* {@code normComponents} array is not large enough to hold
* all the color and alpha components starting at
* {@code normOffset}.
* @param normComponents an array containing normalized components
* @param normOffset the offset into the {@code normComponents}
* array at which to start retrieving normalized components
* @param components an array that receives the components from
* {@code normComponents}
* @param offset the index into {@code components} at which to
* begin storing normalized components from
* {@code normComponents}
* @return an array containing unnormalized color and alpha
* components.
* @throws IllegalArgumentException If the component values for this
* {@code ColorModel} are not conveniently representable in the
* unnormalized form.
* @throws IllegalArgumentException if the length of
* {@code normComponents} minus {@code normOffset}
* is less than {@code numComponents}
* @throws UnsupportedOperationException if the
* constructor of this {@code ColorModel} called the
* {@code super(bits)} constructor, but did not
* override this method. See the constructor,
* {@link #ColorModel(int)}.
*/
public int[] getUnnormalizedComponents(float[] normComponents,
int normOffset,
int[] components, int offset) {
// Make sure that someone isn't using a custom color model
// that called the super(bits) constructor.
if (colorSpace == null) {
throw new UnsupportedOperationException("This method is not supported "+
"by this color model.");
}
if (nBits == null) {
throw new UnsupportedOperationException ("This method is not supported. "+
"Unable to determine #bits per "+
"component.");
}
if ((normComponents.length - normOffset) < numComponents) {
throw new
IllegalArgumentException(
"Incorrect number of components. Expecting "+
numComponents);
}
if (components == null) {
components = new int[offset+numComponents];
}
if (supportsAlpha && isAlphaPremultiplied) {
float normAlpha = normComponents[normOffset+numColorComponents];
for (int i=0; i < numColorComponents; i++) {
components[offset+i] = (int) (normComponents[normOffset+i]
* ((1<<nBits[i]) - 1)
* normAlpha + 0.5f);
}
components[offset+numColorComponents] = (int)
(normAlpha * ((1<<nBits[numColorComponents]) - 1) + 0.5f);
}
else {
for (int i=0; i < numComponents; i++) {
components[offset+i] = (int) (normComponents[normOffset+i]
* ((1<<nBits[i]) - 1) + 0.5f);
}
}
return components;
}
/**
* Returns an array of all of the color/alpha components in normalized
* form, given an unnormalized component array. Unnormalized components
* are unsigned integral values between 0 and 2<sup>n</sup> - 1, where
* n is the number of bits for a particular component. Normalized
* components are float values between a per component minimum and
* maximum specified by the {@code ColorSpace} object for this
* {@code ColorModel}. An {@code IllegalArgumentException}
* will be thrown if color component values for this
* {@code ColorModel} are not conveniently representable in the
* unnormalized form. If the
* {@code normComponents} array is {@code null}, a new array
* will be allocated. The {@code normComponents} array
* will be returned. Color/alpha components are stored in the
* {@code normComponents} array starting at
* {@code normOffset} (even if the array is allocated by this
* method). An {@code ArrayIndexOutOfBoundsException} is thrown
* if the {@code normComponents} array is not {@code null}
* and is not large enough to hold all the color and alpha components
* (starting at {@code normOffset}). An
* {@code IllegalArgumentException} is thrown if the
* {@code components} array is not large enough to hold all the
* color and alpha components starting at {@code offset}.
* <p>
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. The default implementation
* of this method in this abstract class assumes that component values
* for this class are conveniently representable in the unnormalized
* form. Therefore, subclasses which may
* have instances which do not support the unnormalized form must
* override this method.
* @param components an array containing unnormalized components
* @param offset the offset into the {@code components} array at
* which to start retrieving unnormalized components
* @param normComponents an array that receives the normalized components
* @param normOffset the index into {@code normComponents} at
* which to begin storing normalized components
* @return an array containing normalized color and alpha
* components.
* @throws IllegalArgumentException If the component values for this
* {@code ColorModel} are not conveniently representable in the
* unnormalized form.
* @throws UnsupportedOperationException if the
* constructor of this {@code ColorModel} called the
* {@code super(bits)} constructor, but did not
* override this method. See the constructor,
* {@link #ColorModel(int)}.
* @throws UnsupportedOperationException if this method is unable
* to determine the number of bits per component
*/
public float[] getNormalizedComponents(int[] components, int offset,
float[] normComponents,
int normOffset) {
// Make sure that someone isn't using a custom color model
// that called the super(bits) constructor.
if (colorSpace == null) {
throw new UnsupportedOperationException("This method is not supported by "+
"this color model.");
}
if (nBits == null) {
throw new UnsupportedOperationException ("This method is not supported. "+
"Unable to determine #bits per "+
"component.");
}
if ((components.length - offset) < numComponents) {
throw new
IllegalArgumentException(
"Incorrect number of components. Expecting "+
numComponents);
}
if (normComponents == null) {
normComponents = new float[numComponents+normOffset];
}
if (supportsAlpha && isAlphaPremultiplied) {
// Normalized coordinates are non premultiplied
float normAlpha = (float)components[offset+numColorComponents];
normAlpha /= (float) ((1<<nBits[numColorComponents]) - 1);
if (normAlpha != 0.0f) {
for (int i=0; i < numColorComponents; i++) {
normComponents[normOffset+i] =
((float) components[offset+i]) /
(normAlpha * ((float) ((1<<nBits[i]) - 1)));
}
} else {
for (int i=0; i < numColorComponents; i++) {
normComponents[normOffset+i] = 0.0f;
}
}
normComponents[normOffset+numColorComponents] = normAlpha;
}
else {
for (int i=0; i < numComponents; i++) {
normComponents[normOffset+i] = ((float) components[offset+i]) /
((float) ((1<<nBits[i]) - 1));
}
}
return normComponents;
}
/**
* Returns a pixel value represented as an {@code int} in this
* {@code ColorModel}, given an array of unnormalized color/alpha
* components. This method will throw an
* {@code IllegalArgumentException} if component values for this
* {@code ColorModel} are not conveniently representable as a
* single {@code int} or if color component values for this
* {@code ColorModel} are not conveniently representable in the
* unnormalized form. An
* {@code ArrayIndexOutOfBoundsException} is thrown if the
* {@code components} array is not large enough to hold all the
* color and alpha components (starting at {@code offset}).
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param components an array of unnormalized color and alpha
* components
* @param offset the index into {@code components} at which to
* begin retrieving the color and alpha components
* @return an {@code int} pixel value in this
* {@code ColorModel} corresponding to the specified components.
* @throws IllegalArgumentException if
* pixel values for this {@code ColorModel} are not
* conveniently representable as a single {@code int}
* @throws IllegalArgumentException if
* component values for this {@code ColorModel} are not
* conveniently representable in the unnormalized form
* @throws ArrayIndexOutOfBoundsException if
* the {@code components} array is not large enough to
* hold all of the color and alpha components starting at
* {@code offset}
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
*/
public int getDataElement(int[] components, int offset) {
throw new UnsupportedOperationException("This method is not supported "+
"by this color model.");
}
/**
* Returns a data element array representation of a pixel in this
* {@code ColorModel}, given an array of unnormalized color/alpha
* components. This array can then be passed to the
* {@code setDataElements} method of a {@code WritableRaster}
* object. This method will throw an {@code IllegalArgumentException}
* if color component values for this {@code ColorModel} are not
* conveniently representable in the unnormalized form.
* An {@code ArrayIndexOutOfBoundsException} is thrown
* if the {@code components} array is not large enough to hold
* all the color and alpha components (starting at
* {@code offset}). If the {@code obj} variable is
* {@code null}, a new array will be allocated. If
* {@code obj} is not {@code null}, it must be a primitive
* array of type transferType; otherwise, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code obj} is not large enough to hold a pixel value for this
* {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param components an array of unnormalized color and alpha
* components
* @param offset the index into {@code components} at which to
* begin retrieving color and alpha components
* @param obj the {@code Object} representing an array of color
* and alpha components
* @return an {@code Object} representing an array of color and
* alpha components.
* @throws ClassCastException if {@code obj}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code obj} is not large enough to hold a pixel value
* for this {@code ColorModel} or the {@code components}
* array is not large enough to hold all of the color and alpha
* components starting at {@code offset}
* @throws IllegalArgumentException if
* component values for this {@code ColorModel} are not
* conveniently representable in the unnormalized form
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
* @see WritableRaster#setDataElements
* @see SampleModel#setDataElements
*/
public Object getDataElements(int[] components, int offset, Object obj) {
throw new UnsupportedOperationException("This method has not been implemented "+
"for this color model.");
}
/**
* Returns a pixel value represented as an {@code int} in this
* {@code ColorModel}, given an array of normalized color/alpha
* components. This method will throw an
* {@code IllegalArgumentException} if pixel values for this
* {@code ColorModel} are not conveniently representable as a
* single {@code int}. An
* {@code ArrayIndexOutOfBoundsException} is thrown if the
* {@code normComponents} array is not large enough to hold all the
* color and alpha components (starting at {@code normOffset}).
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. The default implementation
* of this method in this abstract class first converts from the
* normalized form to the unnormalized form and then calls
* {@code getDataElement(int[], int)}. Subclasses which may
* have instances which do not support the unnormalized form must
* override this method.
* @param normComponents an array of normalized color and alpha
* components
* @param normOffset the index into {@code normComponents} at which to
* begin retrieving the color and alpha components
* @return an {@code int} pixel value in this
* {@code ColorModel} corresponding to the specified components.
* @throws IllegalArgumentException if
* pixel values for this {@code ColorModel} are not
* conveniently representable as a single {@code int}
* @throws ArrayIndexOutOfBoundsException if
* the {@code normComponents} array is not large enough to
* hold all of the color and alpha components starting at
* {@code normOffset}
* @since 1.4
*/
public int getDataElement(float[] normComponents, int normOffset) {
int[] components = getUnnormalizedComponents(normComponents,
normOffset, null, 0);
return getDataElement(components, 0);
}
/**
* Returns a data element array representation of a pixel in this
* {@code ColorModel}, given an array of normalized color/alpha
* components. This array can then be passed to the
* {@code setDataElements} method of a {@code WritableRaster}
* object. An {@code ArrayIndexOutOfBoundsException} is thrown
* if the {@code normComponents} array is not large enough to hold
* all the color and alpha components (starting at
* {@code normOffset}). If the {@code obj} variable is
* {@code null}, a new array will be allocated. If
* {@code obj} is not {@code null}, it must be a primitive
* array of type transferType; otherwise, a
* {@code ClassCastException} is thrown. An
* {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code obj} is not large enough to hold a pixel value for this
* {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. The default implementation
* of this method in this abstract class first converts from the
* normalized form to the unnormalized form and then calls
* {@code getDataElement(int[], int, Object)}. Subclasses which may
* have instances which do not support the unnormalized form must
* override this method.
* @param normComponents an array of normalized color and alpha
* components
* @param normOffset the index into {@code normComponents} at which to
* begin retrieving color and alpha components
* @param obj a primitive data array to hold the returned pixel
* @return an {@code Object} which is a primitive data array
* representation of a pixel
* @throws ClassCastException if {@code obj}
* is not a primitive array of type {@code transferType}
* @throws ArrayIndexOutOfBoundsException if
* {@code obj} is not large enough to hold a pixel value
* for this {@code ColorModel} or the {@code normComponents}
* array is not large enough to hold all of the color and alpha
* components starting at {@code normOffset}
* @see WritableRaster#setDataElements
* @see SampleModel#setDataElements
* @since 1.4
*/
public Object getDataElements(float[] normComponents, int normOffset,
Object obj) {
int[] components = getUnnormalizedComponents(normComponents,
normOffset, null, 0);
return getDataElements(components, 0, obj);
}
/**
* Returns an array of all of the color/alpha components in normalized
* form, given a pixel in this {@code ColorModel}. The pixel
* value is specified by an array of data elements of type transferType
* passed in as an object reference. If pixel is not a primitive array
* of type transferType, a {@code ClassCastException} is thrown.
* An {@code ArrayIndexOutOfBoundsException} is thrown if
* {@code pixel} is not large enough to hold a pixel value for this
* {@code ColorModel}.
* Normalized components are float values between a per component minimum
* and maximum specified by the {@code ColorSpace} object for this
* {@code ColorModel}. If the
* {@code normComponents} array is {@code null}, a new array
* will be allocated. The {@code normComponents} array
* will be returned. Color/alpha components are stored in the
* {@code normComponents} array starting at
* {@code normOffset} (even if the array is allocated by this
* method). An {@code ArrayIndexOutOfBoundsException} is thrown
* if the {@code normComponents} array is not {@code null}
* and is not large enough to hold all the color and alpha components
* (starting at {@code normOffset}).
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. The default implementation
* of this method in this abstract class first retrieves color and alpha
* components in the unnormalized form using
* {@code getComponents(Object, int[], int)} and then calls
* {@code getNormalizedComponents(int[], int, float[], int)}.
* Subclasses which may
* have instances which do not support the unnormalized form must
* override this method.
* @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}.
* @throws UnsupportedOperationException if the
* constructor of this {@code ColorModel} called the
* {@code super(bits)} constructor, but did not
* override this method. See the constructor,
* {@link #ColorModel(int)}.
* @throws UnsupportedOperationException if this method is unable
* to determine the number of bits per component
* @since 1.4
*/
public float[] getNormalizedComponents(Object pixel,
float[] normComponents,
int normOffset) {
int[] components = getComponents(pixel, null, 0);
return getNormalizedComponents(components, 0,
normComponents, normOffset);
}
/**
* This method simply delegates to the default implementation in {@code Object}
* which is identical to an {@code ==} test since this class cannot enforce the
* issues of a proper equality test among multiple independent subclass
* branches.
* Subclasses are encouraged to override this method and provide equality
* testing for their own properties in addition to equality tests for the
* following common base properties of {@code ColorModel}:
* <ul>
* <li>Support for alpha component.</li>
* <li>Is alpha premultiplied.</li>
* <li>Number of bits per pixel.</li>
* <li>Type of transparency like Opaque, Bitmask or Translucent.</li>
* <li>Number of components in a pixel.</li>
* <li>{@code ColorSpace} type.</li>
* <li>Type of the array used to represent pixel values.</li>
* <li>Number of significant bits per color and alpha component.</li>
* </ul>
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
*/
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* This method simply delegates to the default implementation in {@code Object}
* which returns the system ID for the class.
* Subclasses are encouraged to override this method and provide a hash
* for their own properties in addition to hashing the values of the
* following common base properties of {@code ColorModel}:
* <ul>
* <li>Support for alpha component.</li>
* <li>Is alpha premultiplied.</li>
* <li>Number of bits per pixel.</li>
* <li>Type of transparency like Opaque, Bitmask or Translucent.</li>
* <li>Number of components in a pixel.</li>
* <li>{@code ColorSpace} type.</li>
* <li>Type of the array used to represent pixel values.</li>
* <li>Number of significant bits per color and alpha component.</li>
* </ul>
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Returns the {@code ColorSpace} associated with this
* {@code ColorModel}.
* @return the {@code ColorSpace} of this
* {@code ColorModel}.
*/
public final ColorSpace getColorSpace() {
return colorSpace;
}
/**
* 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 will also return an instance of this
* {@code ColorModel} with the {@code isAlphaPremultiplied}
* flag set appropriately. This method will throw a
* {@code UnsupportedOperationException} if it is not supported
* by this {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param raster the {@code WritableRaster} data
* @param isAlphaPremultiplied {@code true} if the alpha is
* premultiplied; {@code false} otherwise
* @return a {@code ColorModel} object that represents the
* coerced data.
*/
public ColorModel coerceData (WritableRaster raster,
boolean isAlphaPremultiplied) {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
/**
* Returns {@code true} if {@code raster} is compatible
* with this {@code ColorModel} and {@code false} if it is
* not.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param raster the {@link Raster} object to test for compatibility
* @return {@code true} if {@code raster} is compatible
* with this {@code ColorModel}.
* @throws UnsupportedOperationException if this
* method has not been implemented for this
* {@code ColorModel}
*/
public boolean isCompatibleRaster(Raster raster) {
throw new UnsupportedOperationException(
"This method has not been implemented for this ColorModel.");
}
/**
* Creates a {@code WritableRaster} with the specified width and
* height that has a data layout ({@code SampleModel}) compatible
* with this {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param w the width to apply to the new {@code WritableRaster}
* @param h the height to apply to the new {@code WritableRaster}
* @return a {@code WritableRaster} object with the specified
* width and height.
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
* @see WritableRaster
* @see SampleModel
*/
public WritableRaster createCompatibleWritableRaster(int w, int h) {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
/**
* Creates a {@code SampleModel} with the specified width and
* height that has a data layout compatible with this
* {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param w the width to apply to the new {@code SampleModel}
* @param h the height to apply to the new {@code SampleModel}
* @return a {@code SampleModel} object with the specified
* width and height.
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
* @see SampleModel
*/
public SampleModel createCompatibleSampleModel(int w, int h) {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
/** Checks if the {@code SampleModel} is compatible with this
* {@code ColorModel}.
* Since {@code ColorModel} is an abstract class,
* any instance is an instance of a subclass. Subclasses must
* override this method since the implementation in this abstract
* class throws an {@code UnsupportedOperationException}.
* @param sm the specified {@code SampleModel}
* @return {@code true} if the specified {@code SampleModel}
* is compatible with this {@code ColorModel}; {@code false}
* otherwise.
* @throws UnsupportedOperationException if this
* method is not supported by this {@code ColorModel}
* @see SampleModel
*/
public boolean isCompatibleSampleModel(SampleModel sm) {
throw new UnsupportedOperationException
("This method is not supported by this color model");
}
/**
* Disposes of system resources associated with this
* {@code ColorModel} once this {@code ColorModel} is no
* longer referenced.
*
* @deprecated The {@code finalize} method has been deprecated.
* Subclasses that override {@code finalize} in order to perform cleanup
* should be modified to use alternative cleanup mechanisms and
* to remove the overriding {@code finalize} method.
* When overriding the {@code finalize} method, its implementation must explicitly
* ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
* See the specification for {@link Object#finalize()} for further
* information about migration options.
*/
@Deprecated(since = "9", forRemoval = true)
@SuppressWarnings("removal")
public void finalize() {
}
/**
* Returns a {@code Raster} representing the alpha channel of an
* image, extracted from the input {@code Raster}, provided that
* pixel values of this {@code ColorModel} represent color and
* alpha information as separate spatial bands (e.g.
* {@link ComponentColorModel} and {@code DirectColorModel}).
* This method assumes that {@code Raster} objects associated
* with such a {@code ColorModel} store the alpha band, if
* present, as the last band of image data. Returns {@code null}
* if there is no separate spatial alpha channel associated with this
* {@code ColorModel}. If this is an
* {@code IndexColorModel} which has alpha in the lookup table,
* this method will return {@code null} since
* there is no spatially discrete alpha channel.
* This method will create a new {@code Raster} (but will share
* the data array).
* Since {@code ColorModel} is an abstract class, any instance
* is an instance of a subclass. Subclasses must override this
* method to get any behavior other than returning {@code null}
* because the implementation in this abstract class returns
* {@code null}.
* @param raster the specified {@code Raster}
* @return a {@code Raster} representing the alpha channel of
* an image, obtained from the specified {@code Raster}.
*/
public WritableRaster getAlphaRaster(WritableRaster raster) {
return null;
}
/**
* Returns the {@code String} representation of the contents of
* this {@code ColorModel} object.
* @return a {@code String} representing the contents of this
* {@code ColorModel} object.
*/
public String toString() {
return new String("ColorModel: #pixelBits = "+pixel_bits
+ " numComponents = "+numComponents
+ " color space = "+colorSpace
+ " transparency = "+transparency
+ " has alpha = "+supportsAlpha
+ " isAlphaPre = "+isAlphaPremultiplied
);
}
static int getDefaultTransferType(int pixel_bits) {
if (pixel_bits <= 8) {
return DataBuffer.TYPE_BYTE;
} else if (pixel_bits <= 16) {
return DataBuffer.TYPE_USHORT;
} else if (pixel_bits <= 32) {
return DataBuffer.TYPE_INT;
} else {
return DataBuffer.TYPE_UNDEFINED;
}
}
static byte[] l8Tos8 = null; // 8-bit linear to 8-bit non-linear sRGB LUT
static byte[] s8Tol8 = null; // 8-bit non-linear sRGB to 8-bit linear LUT
static byte[] l16Tos8 = null; // 16-bit linear to 8-bit non-linear sRGB LUT
static short[] s8Tol16 = null; // 8-bit non-linear sRGB to 16-bit linear LUT
// Maps to hold LUTs for grayscale conversions
static Map<ICC_ColorSpace, byte[]> g8Tos8Map = null; // 8-bit gray values to 8-bit sRGB values
static Map<ICC_ColorSpace, byte[]> lg16Toog8Map = null; // 16-bit linear to 8-bit "other" gray
static Map<ICC_ColorSpace, byte[]> g16Tos8Map = null; // 16-bit gray values to 8-bit sRGB values
static Map<ICC_ColorSpace, short[]> lg16Toog16Map = null; // 16-bit linear to 16-bit "other" gray
static boolean isLinearRGBspace(ColorSpace cs) {
return cs == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
}
static boolean isLinearGRAYspace(ColorSpace cs) {
return cs == ColorSpace.getInstance(ColorSpace.CS_GRAY);
}
static byte[] getLinearRGB8TosRGB8LUT() {
if (l8Tos8 == null) {
l8Tos8 = new byte[256];
float input, output;
// algorithm for linear RGB to nonlinear sRGB conversion
// is from the IEC 61966-2-1 International Standard,
// Colour Management - Default RGB colour space - sRGB,
// First Edition, 1999-10,
// available for order at http://www.iec.ch
for (int i = 0; i <= 255; i++) {
input = ((float) i) / 255.0f;
if (input <= 0.0031308f) {
output = input * 12.92f;
} else {
output = 1.055f * ((float) Math.pow(input, (1.0 / 2.4)))
- 0.055f;
}
l8Tos8[i] = (byte) Math.round(output * 255.0f);
}
}
return l8Tos8;
}
static byte[] getsRGB8ToLinearRGB8LUT() {
if (s8Tol8 == null) {
s8Tol8 = new byte[256];
float input, output;
// algorithm from IEC 61966-2-1 International Standard
for (int i = 0; i <= 255; i++) {
input = ((float) i) / 255.0f;
if (input <= 0.04045f) {
output = input / 12.92f;
} else {
output = (float) Math.pow((input + 0.055f) / 1.055f, 2.4);
}
s8Tol8[i] = (byte) Math.round(output * 255.0f);
}
}
return s8Tol8;
}
static byte[] getLinearRGB16TosRGB8LUT() {
if (l16Tos8 == null) {
l16Tos8 = new byte[65536];
float input, output;
// algorithm from IEC 61966-2-1 International Standard
for (int i = 0; i <= 65535; i++) {
input = ((float) i) / 65535.0f;
if (input <= 0.0031308f) {
output = input * 12.92f;
} else {
output = 1.055f * ((float) Math.pow(input, (1.0 / 2.4)))
- 0.055f;
}
l16Tos8[i] = (byte) Math.round(output * 255.0f);
}
}
return l16Tos8;
}
static short[] getsRGB8ToLinearRGB16LUT() {
if (s8Tol16 == null) {
s8Tol16 = new short[256];
float input, output;
// algorithm from IEC 61966-2-1 International Standard
for (int i = 0; i <= 255; i++) {
input = ((float) i) / 255.0f;
if (input <= 0.04045f) {
output = input / 12.92f;
} else {
output = (float) Math.pow((input + 0.055f) / 1.055f, 2.4);
}
s8Tol16[i] = (short) Math.round(output * 65535.0f);
}
}
return s8Tol16;
}
/*
* Return a byte LUT that converts 8-bit gray values in the grayCS
* ColorSpace to the appropriate 8-bit sRGB value. I.e., if lut
* is the byte array returned by this method and sval = lut[gval],
* then the sRGB triple (sval,sval,sval) is the best match to gval.
* Cache references to any computed LUT in a Map.
*/
static byte[] getGray8TosRGB8LUT(ICC_ColorSpace grayCS) {
if (isLinearGRAYspace(grayCS)) {
return getLinearRGB8TosRGB8LUT();
}
if (g8Tos8Map != null) {
byte[] g8Tos8LUT = g8Tos8Map.get(grayCS);
if (g8Tos8LUT != null) {
return g8Tos8LUT;
}
}
byte[] g8Tos8LUT = new byte[256];
for (int i = 0; i <= 255; i++) {
g8Tos8LUT[i] = (byte) i;
}
ColorTransform[] transformList = new ColorTransform[2];
PCMM mdl = CMSManager.getModule();
ICC_ColorSpace srgbCS =
(ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
transformList[0] = mdl.createTransform(
grayCS.getProfile(), ColorTransform.Any, ColorTransform.In);
transformList[1] = mdl.createTransform(
srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
ColorTransform t = mdl.createTransform(transformList);
byte[] tmp = t.colorConvert(g8Tos8LUT, null);
for (int i = 0, j= 2; i <= 255; i++, j += 3) {
// All three components of tmp should be equal, since
// the input color space to colorConvert is a gray scale
// space. However, there are slight anomalies in the results.
// Copy tmp starting at index 2, since colorConvert seems
// to be slightly more accurate for the third component!
g8Tos8LUT[i] = tmp[j];
}
if (g8Tos8Map == null) {
g8Tos8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
}
g8Tos8Map.put(grayCS, g8Tos8LUT);
return g8Tos8LUT;
}
/*
* Return a byte LUT that converts 16-bit gray values in the CS_GRAY
* linear gray ColorSpace to the appropriate 8-bit value in the
* grayCS ColorSpace. Cache references to any computed LUT in a Map.
*/
static byte[] getLinearGray16ToOtherGray8LUT(ICC_ColorSpace grayCS) {
if (lg16Toog8Map != null) {
byte[] lg16Toog8LUT = lg16Toog8Map.get(grayCS);
if (lg16Toog8LUT != null) {
return lg16Toog8LUT;
}
}
short[] tmp = new short[65536];
for (int i = 0; i <= 65535; i++) {
tmp[i] = (short) i;
}
ColorTransform[] transformList = new ColorTransform[2];
PCMM mdl = CMSManager.getModule();
ICC_ColorSpace lgCS =
(ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
transformList[0] = mdl.createTransform (
lgCS.getProfile(), ColorTransform.Any, ColorTransform.In);
transformList[1] = mdl.createTransform (
grayCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
ColorTransform t = mdl.createTransform(transformList);
tmp = t.colorConvert(tmp, null);
byte[] lg16Toog8LUT = new byte[65536];
for (int i = 0; i <= 65535; i++) {
// scale unsigned short (0 - 65535) to unsigned byte (0 - 255)
lg16Toog8LUT[i] =
(byte) (((float) (tmp[i] & 0xffff)) * (1.0f /257.0f) + 0.5f);
}
if (lg16Toog8Map == null) {
lg16Toog8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
}
lg16Toog8Map.put(grayCS, lg16Toog8LUT);
return lg16Toog8LUT;
}
/*
* Return a byte LUT that converts 16-bit gray values in the grayCS
* ColorSpace to the appropriate 8-bit sRGB value. I.e., if lut
* is the byte array returned by this method and sval = lut[gval],
* then the sRGB triple (sval,sval,sval) is the best match to gval.
* Cache references to any computed LUT in a Map.
*/
static byte[] getGray16TosRGB8LUT(ICC_ColorSpace grayCS) {
if (isLinearGRAYspace(grayCS)) {
return getLinearRGB16TosRGB8LUT();
}
if (g16Tos8Map != null) {
byte[] g16Tos8LUT = g16Tos8Map.get(grayCS);
if (g16Tos8LUT != null) {
return g16Tos8LUT;
}
}
short[] tmp = new short[65536];
for (int i = 0; i <= 65535; i++) {
tmp[i] = (short) i;
}
ColorTransform[] transformList = new ColorTransform[2];
PCMM mdl = CMSManager.getModule();
ICC_ColorSpace srgbCS =
(ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
transformList[0] = mdl.createTransform (
grayCS.getProfile(), ColorTransform.Any, ColorTransform.In);
transformList[1] = mdl.createTransform (
srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
ColorTransform t = mdl.createTransform(transformList);
tmp = t.colorConvert(tmp, null);
byte[] g16Tos8LUT = new byte[65536];
for (int i = 0, j= 2; i <= 65535; i++, j += 3) {
// All three components of tmp should be equal, since
// the input color space to colorConvert is a gray scale
// space. However, there are slight anomalies in the results.
// Copy tmp starting at index 2, since colorConvert seems
// to be slightly more accurate for the third component!
// scale unsigned short (0 - 65535) to unsigned byte (0 - 255)
g16Tos8LUT[i] =
(byte) (((float) (tmp[j] & 0xffff)) * (1.0f /257.0f) + 0.5f);
}
if (g16Tos8Map == null) {
g16Tos8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
}
g16Tos8Map.put(grayCS, g16Tos8LUT);
return g16Tos8LUT;
}
/*
* Return a short LUT that converts 16-bit gray values in the CS_GRAY
* linear gray ColorSpace to the appropriate 16-bit value in the
* grayCS ColorSpace. Cache references to any computed LUT in a Map.
*/
static short[] getLinearGray16ToOtherGray16LUT(ICC_ColorSpace grayCS) {
if (lg16Toog16Map != null) {
short[] lg16Toog16LUT = lg16Toog16Map.get(grayCS);
if (lg16Toog16LUT != null) {
return lg16Toog16LUT;
}
}
short[] tmp = new short[65536];
for (int i = 0; i <= 65535; i++) {
tmp[i] = (short) i;
}
ColorTransform[] transformList = new ColorTransform[2];
PCMM mdl = CMSManager.getModule();
ICC_ColorSpace lgCS =
(ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
transformList[0] = mdl.createTransform (
lgCS.getProfile(), ColorTransform.Any, ColorTransform.In);
transformList[1] = mdl.createTransform(
grayCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
ColorTransform t = mdl.createTransform(
transformList);
short[] lg16Toog16LUT = t.colorConvert(tmp, null);
if (lg16Toog16Map == null) {
lg16Toog16Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, short[]>(2));
}
lg16Toog16Map.put(grayCS, lg16Toog16LUT);
return lg16Toog16LUT;
}
}