blob: 1b084e1214872dee3eeef9f0b4e60f4631e0b265 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Igor V. Stolyarov
* @version $Revision$
*/
package java.awt.image;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.util.Arrays;
import org.apache.harmony.awt.internal.nls.Messages;
/**
* The class ColorModel.
*
* @since Android 1.0
*/
public abstract class ColorModel implements Transparency {
/**
* The pixel_bits.
*/
protected int pixel_bits; // Pixel length in bits
/**
* The transfer type.
*/
protected int transferType;
/**
* The cs.
*/
ColorSpace cs;
/**
* The has alpha.
*/
boolean hasAlpha;
/**
* The is alpha premultiplied.
*/
boolean isAlphaPremultiplied;
/**
* The transparency.
*/
int transparency;
/**
* The num color components.
*/
int numColorComponents;
/**
* The num components.
*/
int numComponents;
/**
* The bits.
*/
int[] bits; // Array of components masks
/**
* The max values.
*/
int[] maxValues = null; // Max values that may be represent by color
// components
/**
* The max bit length.
*/
int maxBitLength; // Max length color components in bits
/**
* The RG bdefault.
*/
private static ColorModel RGBdefault;
/**
* Instantiates a new color model with the specified values.
*
* @param pixel_bits
* the pixel length in bits.
* @param bits
* the array of component masks.
* @param cspace
* the color space.
* @param hasAlpha
* whether the color model has alpha.
* @param isAlphaPremultiplied
* whether the alpha is pre-multiplied.
* @param transparency
* the transparency strategy, @see java.awt.Transparency.
* @param transferType
* the transfer type (primitive java type to use for the
* components).
*/
protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace, boolean hasAlpha,
boolean isAlphaPremultiplied, int transparency, int transferType) {
if (pixel_bits < 1) {
// awt.26B=The number of bits in the pixel values is less than 1
throw new IllegalArgumentException(Messages.getString("awt.26B")); //$NON-NLS-1$
}
if (bits == null) {
// awt.26C=bits is null
throw new NullPointerException(Messages.getString("awt.26C")); //$NON-NLS-1$
}
int sum = 0;
for (int element : bits) {
if (element < 0) {
// awt.26D=The elements in bits is less than 0
throw new IllegalArgumentException(Messages.getString("awt.26D")); //$NON-NLS-1$
}
sum += element;
}
if (sum < 1) {
// awt.26E=The sum of the number of bits in bits is less than 1
throw new NullPointerException(Messages.getString("awt.26E")); //$NON-NLS-1$
}
if (cspace == null) {
// awt.26F=The cspace is null
throw new IllegalArgumentException(Messages.getString("awt.26F")); //$NON-NLS-1$
}
if (transparency < Transparency.OPAQUE || transparency > Transparency.TRANSLUCENT) {
// awt.270=The transparency is not a valid value
throw new IllegalArgumentException(Messages.getString("awt.270")); //$NON-NLS-1$
}
this.pixel_bits = pixel_bits;
this.bits = bits.clone();
maxValues = new int[bits.length];
maxBitLength = 0;
for (int i = 0; i < maxValues.length; i++) {
maxValues[i] = (1 << bits[i]) - 1;
if (bits[i] > maxBitLength) {
maxBitLength = bits[i];
}
}
cs = cspace;
this.hasAlpha = hasAlpha;
this.isAlphaPremultiplied = isAlphaPremultiplied;
numColorComponents = cs.getNumComponents();
if (hasAlpha) {
numComponents = numColorComponents + 1;
} else {
numComponents = numColorComponents;
}
this.transparency = transparency;
this.transferType = transferType;
}
/**
* Instantiates a new color model with the specified pixel bit depth. The
* transferType is chosen based on the pixel bits, and the other data fields
* are given default values.
*
* @param bits
* the array of component masks.
*/
public ColorModel(int bits) {
if (bits < 1) {
// awt.271=The number of bits in bits is less than 1
throw new IllegalArgumentException(Messages.getString("awt.271")); //$NON-NLS-1$
}
pixel_bits = bits;
transferType = getTransferType(bits);
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
hasAlpha = true;
isAlphaPremultiplied = false;
transparency = Transparency.TRANSLUCENT;
numColorComponents = 3;
numComponents = 4;
this.bits = null;
}
/**
* Gets the data elements from the specified component array, transforming
* them according to rules of the color model.
*
* @param components
* the components.
* @param offset
* the offset in the normComponents array.
* @param obj
* the array that the result is written to: an array of values
* whose length must be the number of components used by the
* color model and whose type depends on the transfer type (based
* on the pixel bit depth), or null to have the appropriate array
* created.
* @return the array of data elements.
*/
public Object getDataElements(int[] components, int offset, Object obj) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the data elements from the specified array of normalized components.
*
* @param normComponents
* the array normalized components.
* @param normOffset
* the offset in the normComponents array.
* @param obj
* the array that the result is written to: an array of values
* whose length must be the number of components used by the
* color model and whose type depends on the transfer type (based
* on the pixel bit depth), or null to have the appropriate array
* created.
* @return the array of data elements.
*/
public Object getDataElements(float[] normComponents, int normOffset, Object obj) {
int unnormComponents[] = getUnnormalizedComponents(normComponents, normOffset, null, 0);
return getDataElements(unnormComponents, 0, obj);
}
/**
* Gets the data elements corresponding to the pixel determined by the RGB
* data.
*
* @param rgb
* the RGB integer value that defines the pixel.
* @param pixel
* the array that the result is written to: an array of values
* whose length must be the number of components used by the
* color model and whose type depends on the transfer type (based
* on the pixel bit depth), or null to have the appropriate array
* created.
* @return the array of data elements.
*/
public Object getDataElements(int rgb, Object pixel) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the child raster corresponding to the alpha channel of the specified
* writable raster, or null if alpha is not supported.
*
* @param raster
* the raster.
* @return the alpha raster.
*/
public WritableRaster getAlphaRaster(WritableRaster raster) {
return null;
}
/**
* Creates a new color model by coercing the data in the writable raster in
* accordance with the alpha strategy of this color model.
*
* @param raster
* the raster.
* @param isAlphaPremultiplied
* whether the alpha is pre-multiplied in this color model
* @return the new color model.
*/
public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
@Override
public String toString() {
// The output format based on 1.5 release behavior.
// It could be reveled such way:
// ColorModel cm = new
// ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB,
// false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
// System.out.println(cm.toString());
return "ColorModel: Color Space = " + cs.toString() + "; has alpha = " //$NON-NLS-1$ //$NON-NLS-2$
+ hasAlpha + "; is alpha premultipied = " //$NON-NLS-1$
+ isAlphaPremultiplied + "; transparency = " + transparency //$NON-NLS-1$
+ "; number color components = " + numColorComponents //$NON-NLS-1$
+ "; pixel bits = " + pixel_bits + "; transfer type = " //$NON-NLS-1$ //$NON-NLS-2$
+ transferType;
}
/**
* Gets the components of the pixel determined by the data array.
*
* @param pixel
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @param components
* the the array where the resulting components are written (or
* null to prompt the method to create the return array).
* @param offset
* the offset that tells where the results should be written in
* the return array.
* @return the array of components.
*/
public int[] getComponents(Object pixel, int[] components, int offset) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the normalized components of the pixel determined by the data array.
*
* @param pixel
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @param normComponents
* the array where the resulting normalized components are
* written (or null to prompt the method to create the return
* array).
* @param normOffset
* the offset that tells where the results should be written in
* the return array.
* @return the array of normalized components.
*/
public float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset) {
if (pixel == null) {
// awt.294=pixel is null
throw new NullPointerException(Messages.getString("awt.294")); //$NON-NLS-1$
}
int unnormComponents[] = getComponents(pixel, null, 0);
return getNormalizedComponents(unnormComponents, 0, normComponents, normOffset);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ColorModel)) {
return false;
}
ColorModel cm = (ColorModel)obj;
return (pixel_bits == cm.getPixelSize() && transferType == cm.getTransferType()
&& cs.getType() == cm.getColorSpace().getType() && hasAlpha == cm.hasAlpha()
&& isAlphaPremultiplied == cm.isAlphaPremultiplied()
&& transparency == cm.getTransparency()
&& numColorComponents == cm.getNumColorComponents()
&& numComponents == cm.getNumComponents() && Arrays.equals(bits, cm
.getComponentSize()));
}
/**
* Gets the red component of the pixel determined by the data array.
*
* @param inData
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @return the red.
*/
public int getRed(Object inData) {
return getRed(constructPixel(inData));
}
/**
* Gets the RGB integer value corresponding to the pixel defined by the data
* array.
*
* @param inData
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @return the integer value that gives the pixel's colors in RGB format.
*/
public int getRGB(Object inData) {
return (getAlpha(inData) << 24 | getRed(inData) << 16 | getGreen(inData) << 8 | getBlue(inData));
}
/**
* Gets the green component of the pixel defined by the data array.
*
* @param inData
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @return the green.
*/
public int getGreen(Object inData) {
return getGreen(constructPixel(inData));
}
/**
* Gets the blue component of the pixel defined by the data array.
*
* @param inData
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @return the blue.
*/
public int getBlue(Object inData) {
return getBlue(constructPixel(inData));
}
/**
* Gets the alpha component of the pixel defined by the data array.
*
* @param inData
* the data array that defines the pixel (whose primitive type
* corresponds to the pixel length in bits.
* @see ColorModel#getTransferType()
* @return the alpha.
*/
public int getAlpha(Object inData) {
return getAlpha(constructPixel(inData));
}
/**
* Creates a compatible writable raster.
*
* @param w
* the width of the desired writable raster.
* @param h
* the height of the desired writable raster.
* @return the writable raster.
*/
public WritableRaster createCompatibleWritableRaster(int w, int h) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Checks if the sample model is compatible with this color model.
*
* @param sm
* the sample model.
* @return true, if the sample model is compatible with this color model.
*/
public boolean isCompatibleSampleModel(SampleModel sm) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Creates the compatible sample model.
*
* @param w
* the width of the desired sample model.
* @param h
* the height of the desired sample model.
* @return the sample model.
*/
public SampleModel createCompatibleSampleModel(int w, int h) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Checks if the specified raster is compatible with this color model.
*
* @param raster
* the raster to inspect.
* @return true, if the raster is compatible with this color model.
*/
public boolean isCompatibleRaster(Raster raster) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the color space of this color model.
*
* @return the color space.
*/
public final ColorSpace getColorSpace() {
return cs;
}
/**
* Gets the normalized components corresponding to the specified
* unnormalized components.
*
* @param components
* the array of unnormalized components.
* @param offset
* the offset where the components should be read from the array
* of unnormalized components.
* @param normComponents
* the array where the resulting normalized components are
* written (or null to prompt the method to create the return
* array).
* @param normOffset
* the offset that tells where the results should be written in
* the return array.
* @return the normalized components.
*/
public float[] getNormalizedComponents(int[] components, int offset, float normComponents[],
int normOffset) {
if (bits == null) {
// awt.26C=bits is null
throw new UnsupportedOperationException(Messages.getString("awt.26C")); //$NON-NLS-1$
}
if (normComponents == null) {
normComponents = new float[numComponents + normOffset];
}
if (hasAlpha && isAlphaPremultiplied) {
float normAlpha = (float)components[offset + numColorComponents]
/ maxValues[numColorComponents];
if (normAlpha != 0.0f) {
for (int i = 0; i < numColorComponents; i++) {
normComponents[normOffset + i] = components[offset + i]
/ (normAlpha * maxValues[i]);
}
normComponents[normOffset + numColorComponents] = normAlpha;
} else {
for (int i = 0; i < numComponents; i++) {
normComponents[normOffset + i] = 0.0f;
}
}
} else {
for (int i = 0; i < numComponents; i++) {
normComponents[normOffset + i] = (float)components[offset + i] / maxValues[i];
}
}
return normComponents;
}
/**
* Gets the data element corresponding to the unnormalized components.
*
* @param components
* the components.
* @param offset
* the offset to start reading the components from the array of
* components.
* @return the data element.
*/
public int getDataElement(int[] components, int offset) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the unnormalized components corresponding to the specified
* normalized components.
*
* @param normComponents
* the array of normalized components.
* @param normOffset
* the offset where the components should be read from the array
* of normalized components.
* @param components
* the array where the resulting unnormalized components are
* written (or null to prompt the method to create the return
* array).
* @param offset
* the offset that tells where the results should be written in
* the return array.
* @return the unnormalized components.
*/
public int[] getUnnormalizedComponents(float normComponents[], int normOffset,
int components[], int offset) {
if (bits == null) {
// awt.26C=bits is null
throw new UnsupportedOperationException(Messages.getString("awt.26C")); //$NON-NLS-1$
}
if (normComponents.length - normOffset < numComponents) {
// awt.273=The length of normComponents minus normOffset is less
// than numComponents
throw new IllegalArgumentException(Messages.getString("awt.273")); //$NON-NLS-1$
}
if (components == null) {
components = new int[numComponents + offset];
} else {
if (components.length - offset < numComponents) {
// awt.272=The length of components minus offset is less than
// numComponents
throw new IllegalArgumentException(Messages.getString("awt.272")); //$NON-NLS-1$
}
}
if (hasAlpha && isAlphaPremultiplied) {
float alpha = normComponents[normOffset + numColorComponents];
for (int i = 0; i < numColorComponents; i++) {
components[offset + i] = (int)(normComponents[normOffset + i] * maxValues[i]
* alpha + 0.5f);
}
components[offset + numColorComponents] = (int)(normComponents[normOffset
+ numColorComponents]
* maxValues[numColorComponents] + 0.5f);
} else {
for (int i = 0; i < numComponents; i++) {
components[offset + i] = (int)(normComponents[normOffset + i] * maxValues[i] + 0.5f);
}
}
return components;
}
/**
* Gets the data element corresponding to the normalized components.
*
* @param normComponents
* the normalized components.
* @param normOffset
* the offset where the normalized components should be read from
* the normalized component array.
* @return the data element.
*/
public int getDataElement(float normComponents[], int normOffset) {
int unnormComponents[] = getUnnormalizedComponents(normComponents, normOffset, null, 0);
return getDataElement(unnormComponents, 0);
}
/**
* Takes a pixel whose data is defined by an integer, and writes the
* corresponding components into the components array, starting from the
* index offset.
*
* @param pixel
* the pixel data.
* @param components
* the data array to write the components to (or null to have the
* method create the return array).
* @param offset
* the offset that determines where the results are written in
* the components array.
* @return the array of components corresponding to the pixel.
*/
public int[] getComponents(int pixel, int components[], int offset) {
throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$
"supported by this ColorModel"); //$NON-NLS-1$
}
/**
* Gets the red component of the pixel determined by the pixel data.
*
* @param pixel
* the pixel.
* @return the red component of the given pixel.
*/
public abstract int getRed(int pixel);
/**
* Takes the pixel data and returns the integer value corresponding to the
* pixel's color in RGB format.
*
* @param pixel
* the pixel data.
* @return the corresponding RGB integer value.
*/
public int getRGB(int pixel) {
return (getAlpha(pixel) << 24 | getRed(pixel) << 16 | getGreen(pixel) << 8 | getBlue(pixel));
}
/**
* Gets the green component of the pixel determined by the pixel data.
*
* @param pixel
* the pixel.
* @return the green component of the given pixel.
*/
public abstract int getGreen(int pixel);
/**
* Gets the size of the desired component of this color model.
*
* @param componentIdx
* the index that determines which component size to get.
* @return the component size corresponding to the index.
* @throws NullPointerException
* if this color model doesn't support an array of separate
* components.
* @throws ArrayIndexOutOfBoundsException
* if the index is negative or greater than or equal to the
* number of components.
*/
public int getComponentSize(int componentIdx) {
if (bits == null) {
// awt.26C=bits is null
throw new NullPointerException(Messages.getString("awt.26C")); //$NON-NLS-1$
}
if (componentIdx < 0 || componentIdx >= bits.length) {
// awt.274=componentIdx is greater than the number of components or
// less than zero
throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.274")); //$NON-NLS-1$
}
return bits[componentIdx];
}
/**
* Gets the blue component of the pixel determined by the pixel data.
*
* @param pixel
* the pixel.
* @return the blue component of the given pixel.
*/
public abstract int getBlue(int pixel);
/**
* Gets the alpha component of the pixel determined by the pixel data.
*
* @param pixel
* the pixel.
* @return the alpha component of the given pixel.
*/
public abstract int getAlpha(int pixel);
/**
* Gets the array of sizes of the different components.
*
* @return the array of sizes of the different components.
*/
public int[] getComponentSize() {
if (bits != null) {
return bits.clone();
}
return null;
}
/**
* Checks if the alpha component is pre-multiplied.
*
* @return true, if the alpha component is pre-multiplied.
*/
public final boolean isAlphaPremultiplied() {
return isAlphaPremultiplied;
}
/**
* Checks whether this color model supports alpha.
*
* @return true, if this color model has alpha.
*/
public final boolean hasAlpha() {
return hasAlpha;
}
@Override
public int hashCode() {
int hash = 0;
int tmp;
if (hasAlpha) {
hash ^= 1;
hash <<= 8;
}
if (isAlphaPremultiplied) {
hash ^= 1;
hash <<= 8;
}
tmp = hash >>> 24;
hash ^= numColorComponents;
hash <<= 8;
hash |= tmp;
tmp = hash >>> 24;
hash ^= transparency;
hash <<= 8;
hash |= tmp;
tmp = hash >>> 24;
hash ^= cs.getType();
hash <<= 8;
hash |= tmp;
tmp = hash >>> 24;
hash ^= pixel_bits;
hash <<= 8;
hash |= tmp;
tmp = hash >>> 24;
hash ^= transferType;
hash <<= 8;
hash |= tmp;
if (bits != null) {
for (int element : bits) {
tmp = hash >>> 24;
hash ^= element;
hash <<= 8;
hash |= tmp;
}
}
return hash;
}
public int getTransparency() {
return transparency;
}
/**
* Gets the transfer type, which is the type of Java primitive value that
* corresponds to the bit length per pixel: either
* {@link DataBuffer#TYPE_BYTE}, {@link DataBuffer#TYPE_USHORT},
* {@link DataBuffer#TYPE_INT}, or {@link DataBuffer#TYPE_UNDEFINED}.
*
* @return the transfer type.
*/
public final int getTransferType() {
return transferType;
}
/**
* Gets the pixel size in bits.
*
* @return the pixel size.
*/
public int getPixelSize() {
return pixel_bits;
}
/**
* Gets the number of components of this color model.
*
* @return the number of components.
*/
public int getNumComponents() {
return numComponents;
}
/**
* Gets the number of color components of this color model.
*
* @return the number color components.
*/
public int getNumColorComponents() {
return numColorComponents;
}
/**
* Gets the default RGB color model.
*
* @return the default RGB color model.
*/
public static ColorModel getRGBdefault() {
if (RGBdefault == null) {
RGBdefault = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
}
return RGBdefault;
}
/*
* Construct INT pixel representation from Object
* @param obj
* @return
*/
/**
* Construct pixel.
*
* @param obj
* the obj.
* @return the int.
*/
private int constructPixel(Object obj) {
int pixel = 0;
switch (getTransferType()) {
case DataBuffer.TYPE_BYTE:
byte[] bPixel = (byte[])obj;
if (bPixel.length > 1) {
// awt.275=This pixel representation is not suuported by tis
// Color Model
throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$
}
pixel = bPixel[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short[] sPixel = (short[])obj;
if (sPixel.length > 1) {
// awt.275=This pixel representation is not suuported by tis
// Color Model
throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$
}
pixel = sPixel[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int[] iPixel = (int[])obj;
if (iPixel.length > 1) {
// awt.275=This pixel representation is not suuported by tis
// Color Model
throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$
}
pixel = iPixel[0];
break;
default:
// awt.22D=This transferType ( {0} ) is not supported by this
// color model
throw new UnsupportedOperationException(Messages.getString("awt.22D", //$NON-NLS-1$
transferType));
}
return pixel;
}
/**
* Gets the transfer type, which is the type of Java primitive value that
* corresponds to the bit length per pixel: either
* {@link DataBuffer#TYPE_BYTE}, {@link DataBuffer#TYPE_USHORT},
* {@link DataBuffer#TYPE_INT}, or {@link DataBuffer#TYPE_UNDEFINED}.
*
* @param bits
* the array of component masks.
* @return the transfer type.
*/
static int getTransferType(int bits) {
if (bits <= 8) {
return DataBuffer.TYPE_BYTE;
} else if (bits <= 16) {
return DataBuffer.TYPE_USHORT;
} else if (bits <= 32) {
return DataBuffer.TYPE_INT;
} else {
return DataBuffer.TYPE_UNDEFINED;
}
}
@Override
public void finalize() {
// This method is added for the API compatibility
// Don't need to call super since Object's finalize is always empty
}
}