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