/*
* @(#)IndexColorModel.java 1.30 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program 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.
*
* This program 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 at /legal/license.txt).
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.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.math.BigInteger;
/**
* The <code>IndexColorModel</code> class is a <code>ColorModel</code>
* class that works with pixel values consisting of a
* single sample that is an index into a fixed colormap in the default
* sRGB color space. The colormap specifies red, green, blue, and
* optional alpha components corresponding to each index. All components
* are represented in the colormap as 8-bit unsigned integral values.
* Some constructors allow the caller to specify "holes" in the colormap
* by indicating which colormap entries are valid and which represent
* unusable colors via the bits set in a <code>BigInteger</code> object.
* This color model is similar to an X11 PseudoColor visual.
* <p>
* Some constructors provide a means to specify an alpha component
* for each pixel in the colormap, while others either provide no
* such means or, in some cases, a flag to indicate whether the
* colormap data contains alpha values. If no alpha is supplied to
* the constructor, an opaque alpha component (alpha = 1.0) is
* assumed for each entry.
* An optional transparent pixel value can be supplied that indicates a
* completely transparent pixel, regardless of any alpha component
* supplied or assumed for that pixel value.
* Note that the color components in the colormap of an
* <code>IndexColorModel</code> objects are never pre-multiplied with
* the alpha components.
* <p>
* <a name="transparency">
* The transparency of an <code>IndexColorModel</code> object is
* determined by examining the alpha components of the colors in the
* colormap and choosing the most specific value after considering
* the optional alpha values and any transparent index specified.
* The transparency value is <code>Transparency.OPAQUE</code>
* only if all valid colors in
* the colormap are opaque and there is no valid transparent pixel.
* If all valid colors
* in the colormap are either completely opaque (alpha = 1.0) or
* completely transparent (alpha = 0.0), which typically occurs when
* a valid transparent pixel is specified,
* the value is <code>Transparency.BITMASK</code>.
* Otherwise, the value is <code>Transparency.TRANSLUCENT</code>, indicating
* that some valid color has an alpha component that is
* neither completely transparent nor completely opaque (0.0 < alpha < 1.0).
* </a>
* <p>
* The index represented by a pixel value is stored in the least
* significant <em>n</em> bits of the pixel representations passed to the
* methods of this class, where <em>n</em> is the pixel size specified to the
* constructor for a particular <code>IndexColorModel</code> object;
* <em>n</em> must be between 1 and 16, inclusive.
* Higher order bits in pixel representations are assumed to be zero.
* For those methods that use a primitive array pixel representation of
* type <code>transferType</code>, the array length is always one.
* The transfer types supported are <code>DataBuffer.TYPE_BYTE</code> and
* <code>DataBuffer.TYPE_USHORT</code>. A single int pixel
* representation is valid for all objects of this class, since it is
* always possible to represent pixel values used with this class in a
* single int. Therefore, methods that use this representation do
* not throw an <code>IllegalArgumentException</code> due to an invalid
* pixel value.
* <p>
* Many of the methods in this class are final. The reason for
* this is that the underlying native graphics code makes assumptions
* about the layout and operation of this class and those assumptions
* are reflected in the implementations of the methods here that are
* marked final. You can subclass this class for other reaons, but
* you cannot override or modify the behaviour of those methods.
*
* @see ColorModel
* @see ColorSpace
* @see DataBuffer
*
* @version 10 Feb 1997
*/
public class IndexColorModel extends ColorModel {
private int rgb[];
private int map_size;
private int transparent_index = -1;
private boolean allgrayopaque;
private BigInteger validBits;
private static int[] opaqueBits = {8, 8, 8};
private static int[] alphaBits = {8, 8, 8, 8};
/**
* Constructs an <code>IndexColorModel</code> from the specified
* arrays of red, green, and blue components. Pixels described
* by this color model all have alpha components of 255
* unnormalized (1.0 normalized), which means they
* are fully opaque. All of the arrays specifying the color
* components must have at least the specified number of entries.
* The <code>ColorSpace</code> is the default sRGB space.
* Since there is no alpha information in any of the arguments
* to this constructor, the transparency value is always
* <code>Transparency.OPAQUE</code>.
* The transfer type is the smallest of <code>DataBuffer.TYPE_BYTE</code>
* or <code>DataBuffer.TYPE_USHORT</code> that can hold a single pixel.
* @param bits the number of bits each pixel occupies
* @param size the size of the color component arrays
* @param r the array of red color components
* @param g the array of green color components
* @param b the array of blue color components
* @throws IllegalArgumentException if <code>bits</code> is less
* than 1 or greater than 16
* @throws IllegalArgumentException if <code>size</code> is less
* than 1
*/
public IndexColorModel(int bits, int size,
byte r[], byte g[], byte b[]) {
super(bits, opaqueBits,
ColorSpace.getInstance(ColorSpace.CS_sRGB),
false, false, OPAQUE,
ColorModel.getDefaultTransferType(bits));
if (bits < 1 || bits > 16) {
throw new IllegalArgumentException("Number of bits must be between"
+" 1 and 16.");
}
setRGBs(size, r, g, b, null);
}
/**
* Constructs an <code>IndexColorModel</code> from the given arrays
* of red, green, and blue components. Pixels described by this color
* model all have alpha components of 255 unnormalized
* (1.0 normalized), which means they are fully opaque, except
* for the indicated transparent pixel. All of the arrays
* specifying the color components must have at least the specified
* number of entries.
* The <code>ColorSpace</code> is the default sRGB space.
* The transparency value may be <code>Transparency.OPAQUE</code> or
* <code>Transparency.BITMASK</code> depending on the arguments, as
* specified in the <a href="#transparency">class description</a> above.
* The transfer type is the smallest of <code>DataBuffer.TYPE_BYTE</code>
* or <code>DataBuffer.TYPE_USHORT</code> that can hold a
* single pixel.
* @param bits the number of bits each pixel occupies
* @param size the size of the color component arrays
* @param r the array of red color components
* @param g the array of green color components
* @param b the array of blue color components
* @param trans the index of the transparent pixel
* @throws IllegalArgumentException if <code>bits</code> is less than
* 1 or greater than 16
* @throws IllegalArgumentException if <code>size</code> is less than
* 1
*/
public IndexColorModel(int bits, int size,
byte r[], byte g[], byte b[], int trans) {
super(bits, opaqueBits,
ColorSpace.getInstance(ColorSpace.CS_sRGB),
false, false, OPAQUE,
ColorModel.getDefaultTransferType(bits));
if (bits < 1 || bits > 16) {
throw new IllegalArgumentException("Number of bits must be between"
+" 1 and 16.");
}
setRGBs(size, r, g, b, null);
setTransparentPixel(trans);
}
/**
* Constructs an <code>IndexColorModel</code> from the given
* arrays of red, green, blue and alpha components. All of the
* arrays specifying the components must have at least the specified
* number of entries.
* The <code>ColorSpace</code> is the default sRGB space.
* The transparency value may be any of <code>Transparency.OPAQUE</code>,
* <code>Transparency.BITMASK</code>,
* or <code>Transparency.TRANSLUCENT</code>
* depending on the arguments, as specified
* in the <a href="#transparency">class description</a> above.
* The transfer type is the smallest of <code>DataBuffer.TYPE_BYTE</code>
* or <code>DataBuffer.TYPE_USHORT</code> that can hold a single pixel.
* @param bits the number of bits each pixel occupies
* @param size the size of the color component arrays
* @param r the array of red color components
* @param g the array of green color components
* @param b the array of blue color components
* @param a the array of alpha value components
* @throws IllegalArgumentException if <code>bits</code> is less
* than 1 or greater than 16
* @throws IllegalArgumentException if <code>size</code> is less
* than 1
*/
public IndexColorModel(int bits, int size,
byte r[], byte g[], byte b[], byte a[]) {
super (bits, alphaBits,
ColorSpace.getInstance(ColorSpace.CS_sRGB),
true, false, TRANSLUCENT,
ColorModel.getDefaultTransferType(bits));
if (bits < 1 || bits > 16) {
throw new IllegalArgumentException("Number of bits must be between"
+" 1 and 16.");
}
setRGBs(size, r, g, b, a);
}
/**
* Constructs an <code>IndexColorModel</code> from a single
* array of interleaved red, green, blue and optional alpha
* components. The array must have enough values in it to
* fill all of the needed component arrays of the specified
* size. The <code>ColorSpace</code> is the default sRGB space.
* The transparency value may be any of <code>Transparency.OPAQUE</code>,
* <code>Transparency.BITMASK</code>,
* or <code>Transparency.TRANSLUCENT</code>
* depending on the arguments, as specified
* in the <a href="#transparency">class description</a> above.
* The transfer type is the smallest of
* <code>DataBuffer.TYPE_BYTE</code> or <code>DataBuffer.TYPE_USHORT</code>
* that can hold a single pixel.
*
* @param bits the number of bits each pixel occupies
* @param size the size of the color component arrays
* @param cmap the array of color components
* @param start the starting offset of the first color component
* @param hasalpha indicates whether alpha values are contained in
* the <code>cmap</code> array
* @throws IllegalArgumentException if <code>bits</code> is less
* than 1 or greater than 16
* @throws IllegalArgumentException if <code>size</code> is less
* than 1
*/
public IndexColorModel(int bits, int size, byte cmap[], int start,
boolean hasalpha) {
this(bits, size, cmap, start, hasalpha, -1);
if (bits < 1 || bits > 16) {
throw new IllegalArgumentException("Number of bits must be between"
+" 1 and 16.");
}
}
/**
* Constructs an <code>IndexColorModel</code> from a single array of
* interleaved red, green, blue and optional alpha components. The
* specified transparent index represents a pixel that is considered
* entirely transparent regardless of any alpha value specified
* for it. The array must have enough values in it to fill all
* of the needed component arrays of the specified size.
* The <code>ColorSpace</code> is the default sRGB space.
* The transparency value may be any of <code>Transparency.OPAQUE</code>,
* <code>Transparency.BITMASK</code>,
* or <code>Transparency.TRANSLUCENT</code>
* depending on the arguments, as specified
* in the <a href="#transparency">class description</a> above.
* The transfer type is the smallest of
* <code>DataBuffer.TYPE_BYTE</code> or <code>DataBuffer.TYPE_USHORT</code>
* that can hold a single pixel.
* @param bits the number of bits each pixel occupies
* @param size the size of the color component arrays
* @param cmap the array of color components
* @param start the starting offset of the first color component
* @param hasalpha indicates whether alpha values are contained in
* the <code>cmap</code> array
* @param trans the index of the fully transparent pixel
* @throws IllegalArgumentException if <code>bits</code> is less than
* 1 or greater than 16
* @throws IllegalArgumentException if <code>size</code> is less than
* 1
*/
public IndexColorModel(int bits, int size, byte cmap[], int start,
boolean hasalpha, int trans) {
// NOTE: This assumes the ordering: RGB[A]
super(bits, opaqueBits,
ColorSpace.getInstance(ColorSpace.CS_sRGB),
false, false, OPAQUE,
ColorModel.getDefaultTransferType(bits));
if (bits < 1 || bits > 16) {
throw new IllegalArgumentException("Number of bits must be between"
+" 1 and 16.");
}
if (size < 1) {
throw new IllegalArgumentException("Map size ("+size+
") must be >= 1");
}
map_size = size;
rgb = new int[calcRealMapSize(bits, size)];
int j = start;
int alpha = 0xff;
boolean allgray = true;
int transparency = OPAQUE;
for (int i = 0; i < size; i++) {
int r = cmap[j++] & 0xff;
int g = cmap[j++] & 0xff;
int b = cmap[j++] & 0xff;
allgray = allgray && (r == g) && (g == b);
if (hasalpha) {
alpha = cmap[j++] & 0xff;
if (alpha != 0xff) {
if (alpha == 0x00) {
if (transparency == OPAQUE) {
transparency = BITMASK;
}
if (transparent_index < 0) {
transparent_index = i;
}
} else {
transparency = TRANSLUCENT;
}
allgray = false;
}
}
rgb[i] = (alpha << 24) | (r << 16) | (g << 8) | b;
}
this.allgrayopaque = allgray;
setTransparency(transparency);
setTransparentPixel(trans);
}
private void setRGBs(int size, byte r[], byte g[], byte b[], byte a[]) {
if (size < 1) {
throw new IllegalArgumentException("Map size ("+size+
") must be >= 1");
}
map_size = size;
rgb = new int[calcRealMapSize(pixel_bits, size)];
int alpha = 0xff;
int transparency = OPAQUE;
boolean allgray = true;
for (int i = 0; i < size; i++) {
int rc = r[i] & 0xff;
int gc = g[i] & 0xff;
int bc = b[i] & 0xff;
allgray = allgray && (rc == gc) && (gc == bc);
if (a != null) {
alpha = a[i] & 0xff;
if (alpha != 0xff) {
if (alpha == 0x00) {
if (transparency == OPAQUE) {
transparency = BITMASK;
}
if (transparent_index < 0) {
transparent_index = i;
}
} else {
transparency = TRANSLUCENT;
}
allgray = false;
}
}
rgb[i] = (alpha << 24) | (rc << 16) | (gc << 8) | bc;
}
this.allgrayopaque = allgray;
setTransparency(transparency);
}
private void setRGBs(int size, int cmap[], int start, boolean hasalpha) {
map_size = size;
rgb = new int[calcRealMapSize(pixel_bits, size)];
int j = start;
int transparency = OPAQUE;
boolean allgray = true;
BigInteger validBits = this.validBits;
for (int i = 0; i < size; i++, j++) {
if (validBits != null && !validBits.testBit(i)) {
continue;
}
int cmaprgb = cmap[j];
int r = (cmaprgb >> 16) & 0xff;
int g = (cmaprgb >> 8) & 0xff;
int b = (cmaprgb ) & 0xff;
allgray = allgray && (r == g) && (g == b);
if (hasalpha) {
int alpha = cmaprgb >>> 24;
if (alpha != 0xff) {
if (alpha == 0x00) {
if (transparency == OPAQUE) {
transparency = BITMASK;
}
if (transparent_index < 0) {
transparent_index = i;
}
} else {
transparency = TRANSLUCENT;
}
allgray = false;
}
} else {
cmaprgb |= 0xff000000;
}
rgb[i] = cmaprgb;
}
this.allgrayopaque = allgray;
setTransparency(transparency);
}
private int calcRealMapSize(int bits, int size) {
int newSize = Math.max(1 << bits, size);
return Math.max(newSize, 256);
}
/**
* Returns the size of the color/alpha component arrays in this
* <code>IndexColorModel</code>.
* @return the size of the color and alpha component arrays.
*/
final public int getMapSize() {
return map_size;
}
/**
* Returns the index of the transparent pixel in this
* <code>IndexColorModel</code> or -1 if there is no transparent pixel.
* @return the index of this <code>IndexColorModel</code> object's
* transparent pixel, or -1 if there is no such pixel.
*/
final public int getTransparentPixel() {
return transparent_index;
}
/**
* Copies the array of red color components into the specified array.
* Only the initial entries of the array as specified by
* {@link #getMapSize() getMapSize} are written.
* @param r the specified array into which the elements of the
* array of red color components are copied
*/
final public void getReds(byte r[]) {
for (int i = 0; i < map_size; i++) {
r[i] = (byte) (rgb[i] >> 16);
}
}
/**
* Copies the array of green color components into the specified array.
* Only the initial entries of the array as specified by
* <code>getMapSize</code> are written.
* @param g the specified array into which the elements of the
* array of green color components are copied
*/
final public void getGreens(byte g[]) {
for (int i = 0; i < map_size; i++) {
g[i] = (byte) (rgb[i] >> 8);
}
}
/**
* Copies the array of blue color components into the specified array.
* Only the initial entries of the array as specified by
* <code>getMapSize</code> are written.
* @param b the specified array into which the elements of the
* array of blue color components are copied
*/
final public void getBlues(byte b[]) {
for (int i = 0; i < map_size; i++) {
b[i] = (byte) rgb[i];
}
}
/**
* Copies the array of alpha transparency components into the
* specified array. Only the initial entries of the array as specified
* by <code>getMapSize</code> are written.
* @param a the specified array into which the elements of the
* array of alpha components are copied
*/
final public void getAlphas(byte a[]) {
for (int i = 0; i < map_size; i++) {
a[i] = (byte) (rgb[i] >> 24);
}
}
/**
* Disposes of system resources associated with this
* <code>ColorModel</code> once this <code>ColorModel</code> is no
* longer referenced.
*/
public void finalize() {
}
private void setTransparentPixel(int trans) {
if (trans >= 0 && trans < map_size) {
rgb[trans] &= 0x00ffffff;
transparent_index = trans;
allgrayopaque = false;
if (this.transparency == OPAQUE) {
setTransparency(BITMASK);
}
}
}
private void setTransparency(int transparency) {
if (this.transparency != transparency) {
this.transparency = transparency;
if (transparency == OPAQUE) {
supportsAlpha = false;
numComponents = 3;
nBits = opaqueBits;
} else {
supportsAlpha = true;
numComponents = 4;
nBits = alphaBits;
}
}
}
/**
* Returns the red color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. The pixel value
* is specified as an int. The returned value is a
* non pre-multiplied value.
* @param pixel the specified pixel
* @return the value of the red color component for the specified pixel
*/
final public int getRed(int pixel) {
return (rgb[pixel] >> 16) & 0xff;
}
/**
* Returns the green color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. The pixel value
* is specified as an int. The returned value is a
* non pre-multiplied value.
* @param pixel the specified pixel
* @return the value of the green color component for the specified pixel
*/
final public int getGreen(int pixel) {
return (rgb[pixel] >> 8) & 0xff;
}
/**
* Returns the blue color component for the specified pixel, scaled
* from 0 to 255 in the default RGB ColorSpace, sRGB. The pixel value
* is specified as an int. The returned value is a
* non pre-multiplied value.
* @param pixel the specified pixel
* @return the value of the blue color component for the specified pixel
*/
final public int getBlue(int pixel) {
return rgb[pixel] & 0xff;
}
/**
* Returns the alpha component for the specified pixel, scaled
* from 0 to 255. The pixel value is specified as an int.
* @param pixel the specified pixel
* @return the value of the alpha component for the specified pixel
*/
final public int getAlpha(int pixel) {
return (rgb[pixel] >> 24) & 0xff;
}
/**
* Returns the color/alpha components of the pixel in the default
* RGB color model format. The pixel value is specified as an int.
* The returned value is in a non pre-multiplied format.
* @param pixel the specified pixel
* @return the color and alpha components of the specified pixel
* @see ColorModel#getRGBdefault
*/
final public int getRGB(int pixel) {
return rgb[pixel];
}
/**
* Returns the <code>String</code> representation of the contents of
* this <code>ColorModel</code>object.
* @return a <code>String</code> representing the contents of this
* <code>ColorModel</code> object.
*/
public String toString() {
return new String("IndexColorModel: #pixelBits = "+pixel_bits
+ " numComponents = "+numComponents
+ " color space = "+colorSpace
+ " transparency = "+transparency
+ " transIndex = "+transparent_index
+ " has alpha = "+supportsAlpha
+ " isAlphaPre = "+isAlphaPremultiplied
);
}
}