/* |
|
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package 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 |
|
* pixel to be made completely transparent, 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> |
|
* If an <code>IndexColorModel</code> object has |
|
* a transparency value of <code>Transparency.OPAQUE</code>, |
|
* then the <code>hasAlpha</code> |
|
* and <code>getNumComponents</code> methods |
|
* (both inherited from <code>ColorModel</code>) |
|
* return false and 3, respectively. |
|
* For any other transparency value, |
|
* <code>hasAlpha</code> returns true |
|
* and <code>getNumComponents</code> returns 4. |
|
* |
|
* <p> |
|
* <a name="index_values"> |
|
* The values used to index into the colormap are taken from the least |
|
* significant <em>n</em> bits of pixel representations where |
|
* <em>n</em> is based on the pixel size specified in the constructor. |
|
* For pixel sizes smaller than 8 bits, <em>n</em> is rounded up to a |
|
* power of two (3 becomes 4 and 5,6,7 become 8). |
|
* For pixel sizes between 8 and 16 bits, <em>n</em> is equal to the |
|
* pixel size. |
|
* Pixel sizes larger than 16 bits are not supported by this class. |
|
* Higher order bits beyond <em>n</em> are ignored in pixel representations. |
|
* Index values greater than or equal to the map size, but less than |
|
* 2<sup><em>n</em></sup>, are undefined and return 0 for all color and |
|
* alpha components. |
|
* </a> |
|
* <p> |
|
* 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 reasons, but |
|
* you cannot override or modify the behaviour of those methods. |
|
* |
|
* @see ColorModel |
|
* @see ColorSpace |
|
* @see DataBuffer |
|
* |
|
*/ |
|
public class IndexColorModel extends ColorModel { |
|
private int rgb[]; |
|
private int map_size; |
|
private int pixel_mask; |
|
private int transparent_index = -1; |
|
private boolean allgrayopaque; |
|
private BigInteger validBits; |
|
private sun.awt.image.BufImgSurfaceData.ICMColorData colorData = null; |
|
private static int[] opaqueBits = {8, 8, 8}; |
|
private static int[] alphaBits = {8, 8, 8, 8}; |
|
static private native void initIDs(); |
|
static { |
|
ColorModel.loadLibraries(); |
|
initIDs(); |
|
} |
|
/** |
|
* 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); |
|
calculatePixelMask(); |
|
} |
|
/** |
|
* 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 pixel to be made transparent. 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); |
|
calculatePixelMask(); |
|
} |
|
/** |
|
* 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); |
|
calculatePixelMask(); |
|
} |
|
/** |
|
* 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 made |
|
* 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) { |
|
// REMIND: 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); |
|
calculatePixelMask(); |
|
} |
|
/** |
|
* Constructs an <code>IndexColorModel</code> from an array of |
|
* ints where each int is comprised of red, green, blue, and |
|
* optional alpha components in the default RGB color model format. |
|
* The specified transparent index represents a pixel that is made |
|
* 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. |
|
* @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 |
|
* @param transferType the data type of the array used to represent |
|
* pixel values. The data type must be either |
|
* <code>DataBuffer.TYPE_BYTE</code> or |
|
* <code>DataBuffer.TYPE_USHORT</code>. |
|
* @throws IllegalArgumentException if <code>bits</code> is less |
|
* than 1 or greater than 16 |
|
* @throws IllegalArgumentException if <code>size</code> is less |
|
* than 1 |
|
* @throws IllegalArgumentException if <code>transferType</code> is not |
|
* one of <code>DataBuffer.TYPE_BYTE</code> or |
|
* <code>DataBuffer.TYPE_USHORT</code> |
|
*/ |
|
public IndexColorModel(int bits, int size, |
|
int cmap[], int start, |
|
boolean hasalpha, int trans, int transferType) { |
|
// REMIND: This assumes the ordering: RGB[A] |
|
super(bits, opaqueBits, |
|
ColorSpace.getInstance(ColorSpace.CS_sRGB), |
|
false, false, OPAQUE, |
|
transferType); |
|
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"); |
|
} |
|
if ((transferType != DataBuffer.TYPE_BYTE) && |
|
(transferType != DataBuffer.TYPE_USHORT)) { |
|
throw new IllegalArgumentException("transferType must be either" + |
|
"DataBuffer.TYPE_BYTE or DataBuffer.TYPE_USHORT"); |
|
} |
|
setRGBs(size, cmap, start, hasalpha); |
|
setTransparentPixel(trans); |
|
calculatePixelMask(); |
|
} |
|
/** |
|
* Constructs an <code>IndexColorModel</code> from an |
|
* <code>int</code> array where each <code>int</code> is |
|
* comprised of red, green, blue, and alpha |
|
* components in the default RGB color model format. |
|
* 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 must be one of <code>DataBuffer.TYPE_BYTE</code> |
|
* <code>DataBuffer.TYPE_USHORT</code>. |
|
* The <code>BigInteger</code> object specifies the valid/invalid pixels |
|
* in the <code>cmap</code> array. A pixel is valid if the |
|
* <code>BigInteger</code> value at that index is set, and is invalid |
|
* if the <code>BigInteger</code> bit at that index is not set. |
|
* @param bits the number of bits each pixel occupies |
|
* @param size the size of the color component array |
|
* @param cmap the array of color components |
|
* @param start the starting offset of the first color component |
|
* @param transferType the specified data type |
|
* @param validBits a <code>BigInteger</code> object. If a bit is |
|
* set in the BigInteger, the pixel at that index is valid. |
|
* If a bit is not set, the pixel at that index |
|
* is considered invalid. If null, all pixels are valid. |
|
* Only bits from 0 to the map size are considered. |
|
* @throws IllegalArgumentException if <code>bits</code> is less |
|
* than 1 or greater than 16 |
|
* @throws IllegalArgumentException if <code>size</code> is less |
|
* than 1 |
|
* @throws IllegalArgumentException if <code>transferType</code> is not |
|
* one of <code>DataBuffer.TYPE_BYTE</code> or |
|
* <code>DataBuffer.TYPE_USHORT</code> |
|
* |
|
* @since 1.3 |
|
*/ |
|
public IndexColorModel(int bits, int size, int cmap[], int start, |
|
int transferType, BigInteger validBits) { |
|
super (bits, alphaBits, |
|
ColorSpace.getInstance(ColorSpace.CS_sRGB), |
|
true, false, TRANSLUCENT, |
|
transferType); |
|
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"); |
|
} |
|
if ((transferType != DataBuffer.TYPE_BYTE) && |
|
(transferType != DataBuffer.TYPE_USHORT)) { |
|
throw new IllegalArgumentException("transferType must be either" + |
|
"DataBuffer.TYPE_BYTE or DataBuffer.TYPE_USHORT"); |
|
} |
|
if (validBits != null) { |
|
// Check to see if it is all valid |
|
for (int i=0; i < size; i++) { |
|
if (!validBits.testBit(i)) { |
|
this.validBits = validBits; |
|
break; |
|
} |
|
} |
|
} |
|
setRGBs(size, cmap, start, true); |
|
calculatePixelMask(); |
|
} |
|
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); |
|
} |
|
private BigInteger getAllValid() { |
|
int numbytes = (map_size+7)/8; |
|
byte[] valid = new byte[numbytes]; |
|
java.util.Arrays.fill(valid, (byte)0xff); |
|
valid[0] = (byte)(0xff >>> (numbytes*8 - map_size)); |
|
return new BigInteger(1, valid); |
|
} |
|
/** |
|
* Returns the transparency. Returns either OPAQUE, BITMASK, |
|
* or TRANSLUCENT |
|
* @return the transparency of this <code>IndexColorModel</code> |
|
* @see Transparency#OPAQUE |
|
* @see Transparency#BITMASK |
|
* @see Transparency#TRANSLUCENT |
|
*/ |
|
public int getTransparency() { |
|
return transparency; |
|
} |
|
/** |
|
* Returns an array of the number of bits for each color/alpha component. |
|
* The array contains the color components in the order red, green, |
|
* blue, followed by the alpha component, if present. |
|
* @return an array containing the number of bits of each color |
|
* and alpha component of this <code>IndexColorModel</code> |
|
*/ |
|
public int[] getComponentSize() { |
|
if (nBits == null) { |
|
if (supportsAlpha) { |
|
nBits = new int[4]; |
|
nBits[3] = 8; |
|
} |
|
else { |
|
nBits = new int[3]; |
|
} |
|
nBits[0] = nBits[1] = nBits[2] = 8; |
|
} |
|
return nBits.clone(); |
|
} |
|
/** |
|
* 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 a transparent pixel in this |
|
* <code>IndexColorModel</code> or -1 if there is no pixel |
|
* with an alpha value of 0. If a transparent pixel was |
|
* explicitly specified in one of the constructors by its |
|
* index, then that index will be preferred, otherwise, |
|
* the index of any pixel which happens to be fully transparent |
|
* may be returned. |
|
* @return the index of a transparent pixel in this |
|
* <code>IndexColorModel</code> object, 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); |
|
} |
|
} |
|
/** |
|
* Converts data for each index from the color and alpha component |
|
* arrays to an int in the default RGB ColorModel format and copies |
|
* the resulting 32-bit ARGB values into the specified array. Only |
|
* the initial entries of the array as specified by |
|
* <code>getMapSize</code> are |
|
* written. |
|
* @param rgb the specified array into which the converted ARGB |
|
* values from this array of color and alpha components |
|
* are copied. |
|
*/ |
|
final public void getRGBs(int rgb[]) { |
|
System.arraycopy(this.rgb, 0, rgb, 0, map_size); |
|
} |
|
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; |
|
} |
|
} |
|
} |
|
/** |
|
* This method is called from the constructors to set the pixel_mask |
|
* value, which is based on the value of pixel_bits. The pixel_mask |
|
* value is used to mask off the pixel parameters for methods such |
|
* as getRed(), getGreen(), getBlue(), getAlpha(), and getRGB(). |
|
*/ |
|
private final void calculatePixelMask() { |
|
// Note that we adjust the mask so that our masking behavior here |
|
// is consistent with that of our native rendering loops. |
|
int maskbits = pixel_bits; |
|
if (maskbits == 3) { |
|
maskbits = 4; |
|
} else if (maskbits > 4 && maskbits < 8) { |
|
maskbits = 8; |
|
} |
|
pixel_mask = (1 << maskbits) - 1; |
|
} |
|
/** |
|
* 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. |
|
* Only the lower <em>n</em> bits of the pixel value, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* calculate the returned value. |
|
* 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 & pixel_mask] >> 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. |
|
* Only the lower <em>n</em> bits of the pixel value, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* calculate the returned value. |
|
* 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 & pixel_mask] >> 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. |
|
* Only the lower <em>n</em> bits of the pixel value, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* calculate the returned value. |
|
* 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 & pixel_mask] & 0xff; |
|
} |
|
/** |
|
* Returns the alpha component for the specified pixel, scaled |
|
* from 0 to 255. The pixel value is specified as an int. |
|
* Only the lower <em>n</em> bits of the pixel value, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* calculate the returned value. |
|
* @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 & pixel_mask] >> 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. |
|
* Only the lower <em>n</em> bits of the pixel value, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* calculate the returned value. |
|
* 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 & pixel_mask]; |
|
} |
|
private static final int CACHESIZE = 40; |
|
private int lookupcache[] = new int[CACHESIZE]; |
|
/** |
|
* Returns a data element array representation of a pixel in this |
|
* ColorModel, given an integer pixel representation in the |
|
* default RGB color model. This array can then be passed to the |
|
* {@link WritableRaster#setDataElements(int, int, java.lang.Object) setDataElements} |
|
* method of a {@link WritableRaster} object. If the pixel variable is |
|
* <code>null</code>, a new array is allocated. If <code>pixel</code> |
|
* is not <code>null</code>, it must be |
|
* a primitive array of type <code>transferType</code>; otherwise, a |
|
* <code>ClassCastException</code> is thrown. An |
|
* <code>ArrayIndexOutOfBoundsException</code> is |
|
* thrown if <code>pixel</code> is not large enough to hold a pixel |
|
* value for this <code>ColorModel</code>. The pixel array is returned. |
|
* <p> |
|
* Since <code>IndexColorModel</code> can be subclassed, subclasses |
|
* inherit the implementation of this method and if they don't |
|
* override it then they throw an exception if they use an |
|
* unsupported <code>transferType</code>. |
|
* |
|
* @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>IndexColorModel</code>. |
|
* @throws ClassCastException if <code>pixel</code> |
|
* is not a primitive array of type <code>transferType</code> |
|
* @throws ArrayIndexOutOfBoundsException if |
|
* <code>pixel</code> is not large enough to hold a pixel value |
|
* for this <code>ColorModel</code> |
|
* @throws UnsupportedOperationException if <code>transferType</code> |
|
* is invalid |
|
* @see WritableRaster#setDataElements |
|
* @see SampleModel#setDataElements |
|
*/ |
|
public synchronized Object getDataElements(int rgb, Object pixel) { |
|
int red = (rgb>>16) & 0xff; |
|
int green = (rgb>>8) & 0xff; |
|
int blue = rgb & 0xff; |
|
int alpha = (rgb>>>24); |
|
int pix = 0; |
|
// Note that pixels are stored at lookupcache[2*i] |
|
// and the rgb that was searched is stored at |
|
// lookupcache[2*i+1]. Also, the pixel is first |
|
// inverted using the unary complement operator |
|
// before storing in the cache so it can never be 0. |
|
for (int i = CACHESIZE - 2; i >= 0; i -= 2) { |
|
if ((pix = lookupcache[i]) == 0) { |
|
break; |
|
} |
|
if (rgb == lookupcache[i+1]) { |
|
return installpixel(pixel, ~pix); |
|
} |
|
} |
|
if (allgrayopaque) { |
|
// IndexColorModel objects are all tagged as |
|
// non-premultiplied so ignore the alpha value |
|
// of the incoming color, convert the |
|
// non-premultiplied color components to a |
|
// grayscale value and search for the closest |
|
// gray value in the palette. Since all colors |
|
// in the palette are gray, we only need compare |
|
// to one of the color components for a match |
|
// using a simple linear distance formula. |
|
int minDist = 256; |
|
int d; |
|
int gray = (int) (red*77 + green*150 + blue*29 + 128)/256; |
|
for (int i = 0; i < map_size; i++) { |
|
if (this.rgb[i] == 0x0) { |
|
// For allgrayopaque colormaps, entries are 0 |
|
// iff they are an invalid color and should be |
|
// ignored during color searches. |
|
continue; |
|
} |
|
d = (this.rgb[i] & 0xff) - gray; |
|
if (d < 0) d = -d; |
|
if (d < minDist) { |
|
pix = i; |
|
if (d == 0) { |
|
break; |
|
} |
|
minDist = d; |
|
} |
|
} |
|
} else if (transparency == OPAQUE) { |
|
// IndexColorModel objects are all tagged as |
|
// non-premultiplied so ignore the alpha value |
|
// of the incoming color and search for closest |
|
// color match independently using a 3 component |
|
// Euclidean distance formula. |
|
// For opaque colormaps, palette entries are 0 |
|
// iff they are an invalid color and should be |
|
// ignored during color searches. |
|
// As an optimization, exact color searches are |
|
// likely to be fairly common in opaque colormaps |
|
// so first we will do a quick search for an |
|
// exact match. |
|
int smallestError = Integer.MAX_VALUE; |
|
int lut[] = this.rgb; |
|
int lutrgb; |
|
for (int i=0; i < map_size; i++) { |
|
lutrgb = lut[i]; |
|
if (lutrgb == rgb && lutrgb != 0) { |
|
pix = i; |
|
smallestError = 0; |
|
break; |
|
} |
|
} |
|
if (smallestError != 0) { |
|
for (int i=0; i < map_size; i++) { |
|
lutrgb = lut[i]; |
|
if (lutrgb == 0) { |
|
continue; |
|
} |
|
int tmp = ((lutrgb >> 16) & 0xff) - red; |
|
int currentError = tmp*tmp; |
|
if (currentError < smallestError) { |
|
tmp = ((lutrgb >> 8) & 0xff) - green; |
|
currentError += tmp * tmp; |
|
if (currentError < smallestError) { |
|
tmp = (lutrgb & 0xff) - blue; |
|
currentError += tmp * tmp; |
|
if (currentError < smallestError) { |
|
pix = i; |
|
smallestError = currentError; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} else if (alpha == 0 && transparent_index >= 0) { |
|
// Special case - transparent color maps to the |
|
// specified transparent pixel, if there is one |
|
pix = transparent_index; |
|
} else { |
|
// IndexColorModel objects are all tagged as |
|
// non-premultiplied so use non-premultiplied |
|
// color components in the distance calculations. |
|
// Look for closest match using a 4 component |
|
// Euclidean distance formula. |
|
int smallestError = Integer.MAX_VALUE; |
|
int lut[] = this.rgb; |
|
for (int i=0; i < map_size; i++) { |
|
int lutrgb = lut[i]; |
|
if (lutrgb == rgb) { |
|
if (validBits != null && !validBits.testBit(i)) { |
|
continue; |
|
} |
|
pix = i; |
|
break; |
|
} |
|
int tmp = ((lutrgb >> 16) & 0xff) - red; |
|
int currentError = tmp*tmp; |
|
if (currentError < smallestError) { |
|
tmp = ((lutrgb >> 8) & 0xff) - green; |
|
currentError += tmp * tmp; |
|
if (currentError < smallestError) { |
|
tmp = (lutrgb & 0xff) - blue; |
|
currentError += tmp * tmp; |
|
if (currentError < smallestError) { |
|
tmp = (lutrgb >>> 24) - alpha; |
|
currentError += tmp * tmp; |
|
if (currentError < smallestError && |
|
(validBits == null || validBits.testBit(i))) |
|
{ |
|
pix = i; |
|
smallestError = currentError; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
System.arraycopy(lookupcache, 2, lookupcache, 0, CACHESIZE - 2); |
|
lookupcache[CACHESIZE - 1] = rgb; |
|
lookupcache[CACHESIZE - 2] = ~pix; |
|
return installpixel(pixel, pix); |
|
} |
|
private Object installpixel(Object pixel, int pix) { |
|
switch (transferType) { |
|
case DataBuffer.TYPE_INT: |
|
int[] intObj; |
|
if (pixel == null) { |
|
pixel = intObj = new int[1]; |
|
} else { |
|
intObj = (int[]) pixel; |
|
} |
|
intObj[0] = pix; |
|
break; |
|
case DataBuffer.TYPE_BYTE: |
|
byte[] byteObj; |
|
if (pixel == null) { |
|
pixel = byteObj = new byte[1]; |
|
} else { |
|
byteObj = (byte[]) pixel; |
|
} |
|
byteObj[0] = (byte) pix; |
|
break; |
|
case DataBuffer.TYPE_USHORT: |
|
short[] shortObj; |
|
if (pixel == null) { |
|
pixel = shortObj = new short[1]; |
|
} else { |
|
shortObj = (short[]) pixel; |
|
} |
|
shortObj[0] = (short) pix; |
|
break; |
|
default: |
|
throw new UnsupportedOperationException("This method has not been "+ |
|
"implemented for transferType " + transferType); |
|
} |
|
return pixel; |
|
} |
|
/** |
|
* Returns an array of unnormalized color/alpha components for a |
|
* specified pixel in this <code>ColorModel</code>. The pixel value |
|
* is specified as an int. If the <code>components</code> array is <code>null</code>, |
|
* a new array is allocated that contains |
|
* <code>offset + getNumComponents()</code> elements. |
|
* The <code>components</code> array is returned, |
|
* with the alpha component included |
|
* only if <code>hasAlpha</code> returns true. |
|
* Color/alpha components are stored in the <code>components</code> array starting |
|
* at <code>offset</code> even if the array is allocated by this method. |
|
* An <code>ArrayIndexOutOfBoundsException</code> |
|
* is thrown if the <code>components</code> array is not <code>null</code> and is |
|
* not large enough to hold all the color and alpha components |
|
* starting at <code>offset</code>. |
|
* @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</code> 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. |
|
* @see ColorModel#hasAlpha |
|
* @see ColorModel#getNumComponents |
|
*/ |
|
public int[] getComponents(int pixel, int[] components, int offset) { |
|
if (components == null) { |
|
components = new int[offset+numComponents]; |
|
} |
|
// REMIND: Needs to change if different color space |
|
components[offset+0] = getRed(pixel); |
|
components[offset+1] = getGreen(pixel); |
|
components[offset+2] = getBlue(pixel); |
|
if (supportsAlpha && (components.length-offset) > 3) { |
|
components[offset+3] = getAlpha(pixel); |
|
} |
|
return components; |
|
} |
|
/** |
|
* Returns an array of unnormalized color/alpha components for |
|
* a specified pixel in this <code>ColorModel</code>. The pixel |
|
* value is specified by an array of data elements of type |
|
* <code>transferType</code> passed in as an object reference. |
|
* If <code>pixel</code> is not a primitive array of type |
|
* <code>transferType</code>, a <code>ClassCastException</code> |
|
* is thrown. An <code>ArrayIndexOutOfBoundsException</code> |
|
* is thrown if <code>pixel</code> is not large enough to hold |
|
* a pixel value for this <code>ColorModel</code>. If the |
|
* <code>components</code> array is <code>null</code>, a new array |
|
* is allocated that contains |
|
* <code>offset + getNumComponents()</code> elements. |
|
* The <code>components</code> array is returned, |
|
* with the alpha component included |
|
* only if <code>hasAlpha</code> returns true. |
|
* Color/alpha components are stored in the <code>components</code> |
|
* array starting at <code>offset</code> even if the array is |
|
* allocated by this method. An |
|
* <code>ArrayIndexOutOfBoundsException</code> is also |
|
* thrown if the <code>components</code> array is not |
|
* <code>null</code> and is not large enough to hold all the color |
|
* and alpha components starting at <code>offset</code>. |
|
* <p> |
|
* Since <code>IndexColorModel</code> can be subclassed, subclasses |
|
* inherit the implementation of this method and if they don't |
|
* override it then they throw an exception if they use an |
|
* unsupported <code>transferType</code>. |
|
* |
|
* @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</code> 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 ArrayIndexOutOfBoundsException if <code>pixel</code> |
|
* is not large enough to hold a pixel value for this |
|
* <code>ColorModel</code> or if the |
|
* <code>components</code> array is not <code>null</code> |
|
* and is not large enough to hold all the color |
|
* and alpha components starting at <code>offset</code> |
|
* @throws ClassCastException if <code>pixel</code> is not a |
|
* primitive array of type <code>transferType</code> |
|
* @throws UnsupportedOperationException if <code>transferType</code> |
|
* is not one of the supported transfer types |
|
* @see ColorModel#hasAlpha |
|
* @see ColorModel#getNumComponents |
|
*/ |
|
public int[] getComponents(Object pixel, int[] components, int offset) { |
|
int intpixel; |
|
switch (transferType) { |
|
case DataBuffer.TYPE_BYTE: |
|
byte bdata[] = (byte[])pixel; |
|
intpixel = bdata[0] & 0xff; |
|
break; |
|
case DataBuffer.TYPE_USHORT: |
|
short sdata[] = (short[])pixel; |
|
intpixel = sdata[0] & 0xffff; |
|
break; |
|
case DataBuffer.TYPE_INT: |
|
int idata[] = (int[])pixel; |
|
intpixel = idata[0]; |
|
break; |
|
default: |
|
throw new UnsupportedOperationException("This method has not been "+ |
|
"implemented for transferType " + transferType); |
|
} |
|
return getComponents(intpixel, components, offset); |
|
} |
|
/** |
|
* Returns a pixel value represented as an int in this |
|
* <code>ColorModel</code> given an array of unnormalized |
|
* color/alpha components. An |
|
* <code>ArrayIndexOutOfBoundsException</code> |
|
* is thrown if the <code>components</code> array is not large |
|
* enough to hold all of the color and alpha components starting |
|
* at <code>offset</code>. Since |
|
* <code>ColorModel</code> can be subclassed, subclasses inherit the |
|
* implementation of this method and if they don't override it then |
|
* they throw an exception if they use an unsupported transferType. |
|
* @param components an array of unnormalized color and alpha |
|
* components |
|
* @param offset the index into <code>components</code> at which to |
|
* begin retrieving the color and alpha components |
|
* @return an <code>int</code> pixel value in this |
|
* <code>ColorModel</code> corresponding to the specified components. |
|
* @throws ArrayIndexOutOfBoundsException if |
|
* the <code>components</code> array is not large enough to |
|
* hold all of the color and alpha components starting at |
|
* <code>offset</code> |
|
* @throws UnsupportedOperationException if <code>transferType</code> |
|
* is invalid |
|
*/ |
|
public int getDataElement(int[] components, int offset) { |
|
int rgb = (components[offset+0]<<16) |
|
| (components[offset+1]<<8) | (components[offset+2]); |
|
if (supportsAlpha) { |
|
rgb |= (components[offset+3]<<24); |
|
} |
|
else { |
|
rgb |= 0xff000000; |
|
} |
|
Object inData = getDataElements(rgb, null); |
|
int pixel; |
|
switch (transferType) { |
|
case DataBuffer.TYPE_BYTE: |
|
byte bdata[] = (byte[])inData; |
|
pixel = bdata[0] & 0xff; |
|
break; |
|
case DataBuffer.TYPE_USHORT: |
|
short sdata[] = (short[])inData; |
|
pixel = sdata[0]; |
|
break; |
|
case DataBuffer.TYPE_INT: |
|
int idata[] = (int[])inData; |
|
pixel = idata[0]; |
|
break; |
|
default: |
|
throw new UnsupportedOperationException("This method has not been "+ |
|
"implemented for transferType " + transferType); |
|
} |
|
return pixel; |
|
} |
|
/** |
|
* Returns a data element array representation of a pixel in this |
|
* <code>ColorModel</code> given an array of unnormalized color/alpha |
|
* components. This array can then be passed to the |
|
* <code>setDataElements</code> method of a <code>WritableRaster</code> |
|
* object. An <code>ArrayIndexOutOfBoundsException</code> is |
|
* thrown if the |
|
* <code>components</code> array is not large enough to hold all of the |
|
* color and alpha components starting at <code>offset</code>. |
|
* If the pixel variable is <code>null</code>, a new array |
|
* is allocated. If <code>pixel</code> is not <code>null</code>, |
|
* it must be a primitive array of type <code>transferType</code>; |
|
* otherwise, a <code>ClassCastException</code> is thrown. |
|
* An <code>ArrayIndexOutOfBoundsException</code> is thrown if pixel |
|
* is not large enough to hold a pixel value for this |
|
* <code>ColorModel</code>. |
|
* <p> |
|
* Since <code>IndexColorModel</code> can be subclassed, subclasses |
|
* inherit the implementation of this method and if they don't |
|
* override it then they throw an exception if they use an |
|
* unsupported <code>transferType</code> |
|
* |
|
* @param components an array of unnormalized color and alpha |
|
* components |
|
* @param offset the index into <code>components</code> at which to |
|
* begin retrieving color and alpha components |
|
* @param pixel the <code>Object</code> representing an array of color |
|
* and alpha components |
|
* @return an <code>Object</code> representing an array of color and |
|
* alpha components. |
|
* @throws ClassCastException if <code>pixel</code> |
|
* is not a primitive array of type <code>transferType</code> |
|
* @throws ArrayIndexOutOfBoundsException if |
|
* <code>pixel</code> is not large enough to hold a pixel value |
|
* for this <code>ColorModel</code> or the <code>components</code> |
|
* array is not large enough to hold all of the color and alpha |
|
* components starting at <code>offset</code> |
|
* @throws UnsupportedOperationException if <code>transferType</code> |
|
* is not one of the supported transfer types |
|
* @see WritableRaster#setDataElements |
|
* @see SampleModel#setDataElements |
|
*/ |
|
public Object getDataElements(int[] components, int offset, Object pixel) { |
|
int rgb = (components[offset+0]<<16) | (components[offset+1]<<8) |
|
| (components[offset+2]); |
|
if (supportsAlpha) { |
|
rgb |= (components[offset+3]<<24); |
|
} |
|
else { |
|
rgb &= 0xff000000; |
|
} |
|
return getDataElements(rgb, pixel); |
|
} |
|
/** |
|
* Creates a <code>WritableRaster</code> with the specified width |
|
* and height that has a data layout (<code>SampleModel</code>) |
|
* compatible with this <code>ColorModel</code>. This method |
|
* only works for color models with 16 or fewer bits per pixel. |
|
* <p> |
|
* Since <code>IndexColorModel</code> can be subclassed, any |
|
* subclass that supports greater than 16 bits per pixel must |
|
* override this method. |
|
* |
|
* @param w the width to apply to the new <code>WritableRaster</code> |
|
* @param h the height to apply to the new <code>WritableRaster</code> |
|
* @return a <code>WritableRaster</code> object with the specified |
|
* width and height. |
|
* @throws UnsupportedOperationException if the number of bits in a |
|
* pixel is greater than 16 |
|
* @see WritableRaster |
|
* @see SampleModel |
|
*/ |
|
public WritableRaster createCompatibleWritableRaster(int w, int h) { |
|
WritableRaster raster; |
|
if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4) { |
|
// TYPE_BINARY |
|
raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, |
|
w, h, 1, pixel_bits, null); |
|
} |
|
else if (pixel_bits <= 8) { |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, |
|
w,h,1,null); |
|
} |
|
else if (pixel_bits <= 16) { |
|
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, |
|
w,h,1,null); |
|
} |
|
else { |
|
throw new |
|
UnsupportedOperationException("This method is not supported "+ |
|
" for pixel bits > 16."); |
|
} |
|
return raster; |
|
} |
|
/** |
|
* Returns <code>true</code> if <code>raster</code> is compatible |
|
* with this <code>ColorModel</code> or <code>false</code> if it |
|
* is not compatible with this <code>ColorModel</code>. |
|
* @param raster the {@link Raster} object to test for compatibility |
|
* @return <code>true</code> if <code>raster</code> is compatible |
|
* with this <code>ColorModel</code>; <code>false</code> otherwise. |
|
* |
|
*/ |
|
public boolean isCompatibleRaster(Raster raster) { |
|
int size = raster.getSampleModel().getSampleSize(0); |
|
return ((raster.getTransferType() == transferType) && |
|
(raster.getNumBands() == 1) && ((1 << size) >= map_size)); |
|
} |
|
/** |
|
* Creates a <code>SampleModel</code> with the specified |
|
* width and height that has a data layout compatible with |
|
* this <code>ColorModel</code>. |
|
* @param w the width to apply to the new <code>SampleModel</code> |
|
* @param h the height to apply to the new <code>SampleModel</code> |
|
* @return a <code>SampleModel</code> object with the specified |
|
* width and height. |
|
* @throws IllegalArgumentException if <code>w</code> or |
|
* <code>h</code> is not greater than 0 |
|
* @see SampleModel |
|
*/ |
|
public SampleModel createCompatibleSampleModel(int w, int h) { |
|
int[] off = new int[1]; |
|
off[0] = 0; |
|
if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4) { |
|
return new MultiPixelPackedSampleModel(transferType, w, h, |
|
pixel_bits); |
|
} |
|
else { |
|
return new ComponentSampleModel(transferType, w, h, 1, w, |
|
off); |
|
} |
|
} |
|
/** |
|
* Checks if the specified <code>SampleModel</code> is compatible |
|
* with this <code>ColorModel</code>. If <code>sm</code> is |
|
* <code>null</code>, this method returns <code>false</code>. |
|
* @param sm the specified <code>SampleModel</code>, |
|
* or <code>null</code> |
|
* @return <code>true</code> if the specified <code>SampleModel</code> |
|
* is compatible with this <code>ColorModel</code>; <code>false</code> |
|
* otherwise. |
|
* @see SampleModel |
|
*/ |
|
public boolean isCompatibleSampleModel(SampleModel sm) { |
|
// fix 4238629 |
|
if (! (sm instanceof ComponentSampleModel) && |
|
! (sm instanceof MultiPixelPackedSampleModel) ) { |
|
return false; |
|
} |
|
// Transfer type must be the same |
|
if (sm.getTransferType() != transferType) { |
|
return false; |
|
} |
|
if (sm.getNumBands() != 1) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
/** |
|
* Returns a new <code>BufferedImage</code> of TYPE_INT_ARGB or |
|
* TYPE_INT_RGB that has a <code>Raster</code> with pixel data |
|
* computed by expanding the indices in the source <code>Raster</code> |
|
* using the color/alpha component arrays of this <code>ColorModel</code>. |
|
* Only the lower <em>n</em> bits of each index value in the source |
|
* <code>Raster</code>, as specified in the |
|
* <a href="#index_values">class description</a> above, are used to |
|
* compute the color/alpha values in the returned image. |
|
* If <code>forceARGB</code> is <code>true</code>, a TYPE_INT_ARGB image is |
|
* returned regardless of whether or not this <code>ColorModel</code> |
|
* has an alpha component array or a transparent pixel. |
|
* @param raster the specified <code>Raster</code> |
|
* @param forceARGB if <code>true</code>, the returned |
|
* <code>BufferedImage</code> is TYPE_INT_ARGB; otherwise it is |
|
* TYPE_INT_RGB |
|
* @return a <code>BufferedImage</code> created with the specified |
|
* <code>Raster</code> |
|
* @throws IllegalArgumentException if the raster argument is not |
|
* compatible with this IndexColorModel |
|
*/ |
|
public BufferedImage convertToIntDiscrete(Raster raster, |
|
boolean forceARGB) { |
|
ColorModel cm; |
|
if (!isCompatibleRaster(raster)) { |
|
throw new IllegalArgumentException("This raster is not compatible" + |
|
"with this IndexColorModel."); |
|
} |
|
if (forceARGB || transparency == TRANSLUCENT) { |
|
cm = ColorModel.getRGBdefault(); |
|
} |
|
else if (transparency == BITMASK) { |
|
cm = new DirectColorModel(25, 0xff0000, 0x00ff00, 0x0000ff, |
|
0x1000000); |
|
} |
|
else { |
|
cm = new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff); |
|
} |
|
int w = raster.getWidth(); |
|
int h = raster.getHeight(); |
|
WritableRaster discreteRaster = |
|
cm.createCompatibleWritableRaster(w, h); |
|
Object obj = null; |
|
int[] data = null; |
|
int rX = raster.getMinX(); |
|
int rY = raster.getMinY(); |
|
for (int y=0; y < h; y++, rY++) { |
|
obj = raster.getDataElements(rX, rY, w, 1, obj); |
|
if (obj instanceof int[]) { |
|
data = (int[])obj; |
|
} else { |
|
data = DataBuffer.toIntArray(obj); |
|
} |
|
for (int x=0; x < w; x++) { |
|
data[x] = rgb[data[x] & pixel_mask]; |
|
} |
|
discreteRaster.setDataElements(0, y, w, 1, data); |
|
} |
|
return new BufferedImage(cm, discreteRaster, false, null); |
|
} |
|
/** |
|
* Returns whether or not the pixel is valid. |
|
* @param pixel the specified pixel value |
|
* @return <code>true</code> if <code>pixel</code> |
|
* is valid; <code>false</code> otherwise. |
|
* @since 1.3 |
|
*/ |
|
public boolean isValid(int pixel) { |
|
return ((pixel >= 0 && pixel < map_size) && |
|
(validBits == null || validBits.testBit(pixel))); |
|
} |
|
/** |
|
* Returns whether or not all of the pixels are valid. |
|
* @return <code>true</code> if all pixels are valid; |
|
* <code>false</code> otherwise. |
|
* @since 1.3 |
|
*/ |
|
public boolean isValid() { |
|
return (validBits == null); |
|
} |
|
/** |
|
* Returns a <code>BigInteger</code> that indicates the valid/invalid |
|
* pixels in the colormap. A bit is valid if the |
|
* <code>BigInteger</code> value at that index is set, and is invalid |
|
* if the <code>BigInteger</code> value at that index is not set. |
|
* The only valid ranges to query in the <code>BigInteger</code> are |
|
* between 0 and the map size. |
|
* @return a <code>BigInteger</code> indicating the valid/invalid pixels. |
|
* @since 1.3 |
|
*/ |
|
public BigInteger getValidPixels() { |
|
if (validBits == null) { |
|
return getAllValid(); |
|
} |
|
else { |
|
return validBits; |
|
} |
|
} |
|
/** |
|
* Disposes of system resources associated with this |
|
* <code>ColorModel</code> once this <code>ColorModel</code> is no |
|
* longer referenced. |
|
*/ |
|
public void finalize() { |
|
} |
|
/** |
|
* 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 |
|
); |
|
} |
|
} |