blob: 700eb7a39e5e9a3ed65bef3a2e4d1a1d5addcdb1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Igor V. Stolyarov
* @version $Revision$
*/
package java.awt.image;
import java.awt.color.ColorSpace;
import java.awt.Transparency;
import java.util.Arrays;
import org.apache.harmony.awt.gl.color.LUTColorConverter;
import org.apache.harmony.awt.internal.nls.Messages;
/**
* The Class DirectColorModel represents a standard (packed) RGB color model
* with additional support for converting between sRGB color space and 8 or 16
* bit linear RGB color space using lookup tables.
*
* @since Android 1.0
*/
public class DirectColorModel extends PackedColorModel {
/**
* The from_ linea r_ rg b_ lut.
*/
private byte from_LINEAR_RGB_LUT[]; // Lookup table for conversion from
// Linear RGB Color Space into sRGB
/**
* The to_ linea r_8 rg b_ lut.
*/
private byte to_LINEAR_8RGB_LUT[]; // Lookup table for conversion from
// sRGB Color Space into Linear RGB
// 8 bit
/**
* The to_ linea r_16 rg b_ lut.
*/
private short to_LINEAR_16RGB_LUT[]; // Lookup table for conversion from
// sRGB Color Space into Linear RGB
// 16 bit
/**
* The alpha lut.
*/
private byte alphaLUT[]; // Lookup table for scale alpha value
/**
* The color lu ts.
*/
private byte colorLUTs[][]; // Lookup tables for scale color values
/**
* The is_s rgb.
*/
private boolean is_sRGB; // ColorModel has sRGB ColorSpace
/**
* The is_ linea r_ rgb.
*/
private boolean is_LINEAR_RGB; // Color Model has Linear RGB Color
// Space
/**
* The LINEA r_ rg b_ length.
*/
private int LINEAR_RGB_Length; // Linear RGB bit length
/**
* The factor.
*/
private float fFactor; // Scale factor
/**
* Instantiates a new direct color model.
*
* @param space
* the color space.
* @param bits
* the array of component masks.
* @param rmask
* the bitmask corresponding to the red band.
* @param gmask
* the bitmask corresponding to the green band.
* @param bmask
* the bitmask corresponding to the blue band.
* @param amask
* the bitmask corresponding to the alpha band.
* @param isAlphaPremultiplied
* whether the alpha is pre-multiplied in this color model.
* @param transferType
* the transfer type (primitive java type to use for the
* components).
* @throws IllegalArgumentException
* if the number of bits in the combined bitmasks for the color
* bands is less than one or greater than 32.
*/
public DirectColorModel(ColorSpace space, int bits, int rmask, int gmask, int bmask, int amask,
boolean isAlphaPremultiplied, int transferType) {
super(space, bits, rmask, gmask, bmask, amask, isAlphaPremultiplied,
(amask == 0 ? Transparency.OPAQUE : Transparency.TRANSLUCENT), transferType);
initLUTs();
}
/**
* Instantiates a new direct color model, determining the transfer type from
* the bits array, the transparency from the alpha mask, and the default
* color space {@link ColorSpace#CS_sRGB}.
*
* @param bits
* the array of component masks.
* @param rmask
* the bitmask corresponding to the red band.
* @param gmask
* the bitmask corresponding to the green band.
* @param bmask
* the bitmask corresponding to the blue band.
* @param amask
* the bitmask corresponding to the alpha band.
*/
public DirectColorModel(int bits, int rmask, int gmask, int bmask, int amask) {
super(ColorSpace.getInstance(ColorSpace.CS_sRGB), bits, rmask, gmask, bmask, amask, false,
(amask == 0 ? Transparency.OPAQUE : Transparency.TRANSLUCENT), ColorModel
.getTransferType(bits));
initLUTs();
}
/**
* Instantiates a new direct color model with no alpha channel, determining
* the transfer type from the bits array, the default color space
* {@link ColorSpace#CS_sRGB}, and with the transparency set to
* {@link Transparency#OPAQUE}.
*
* @param bits
* the array of component masks.
* @param rmask
* the bitmask corresponding to the red band.
* @param gmask
* the bitmask corresponding to the green band.
* @param bmask
* the bitmask corresponding to the blue band.
*/
public DirectColorModel(int bits, int rmask, int gmask, int bmask) {
this(bits, rmask, gmask, bmask, 0);
}
@Override
public Object getDataElements(int components[], int offset, Object obj) {
int pixel = 0;
for (int i = 0; i < numComponents; i++) {
pixel |= (components[offset + i] << offsets[i]) & componentMasks[i];
}
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[];
if (obj == null) {
ba = new byte[1];
} else {
ba = (byte[])obj;
}
ba[0] = (byte)pixel;
obj = ba;
break;
case DataBuffer.TYPE_USHORT:
short sa[];
if (obj == null) {
sa = new short[1];
} else {
sa = (short[])obj;
}
sa[0] = (short)pixel;
obj = sa;
break;
case DataBuffer.TYPE_INT:
int ia[];
if (obj == null) {
ia = new int[1];
} else {
ia = (int[])obj;
}
ia[0] = pixel;
obj = ia;
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return obj;
}
@Override
public Object getDataElements(int rgb, Object pixel) {
if (equals(ColorModel.getRGBdefault())) {
int ia[];
if (pixel == null) {
ia = new int[1];
} else {
ia = (int[])pixel;
}
ia[0] = rgb;
return ia;
}
int alpha = (rgb >> 24) & 0xff;
int red = (rgb >> 16) & 0xff;
int green = (rgb >> 8) & 0xff;
int blue = rgb & 0xff;
float comp[] = new float[numColorComponents];
float normComp[] = null;
if (is_sRGB || is_LINEAR_RGB) {
if (is_LINEAR_RGB) {
if (LINEAR_RGB_Length == 8) {
red = to_LINEAR_8RGB_LUT[red] & 0xff;
green = to_LINEAR_8RGB_LUT[green] & 0xff;
blue = to_LINEAR_8RGB_LUT[blue] & 0xff;
} else {
red = to_LINEAR_16RGB_LUT[red] & 0xffff;
green = to_LINEAR_16RGB_LUT[green] & 0xffff;
blue = to_LINEAR_16RGB_LUT[blue] & 0xffff;
}
}
comp[0] = red / fFactor;
comp[1] = green / fFactor;
comp[2] = blue / fFactor;
if (!hasAlpha) {
normComp = comp;
} else {
float normAlpha = alpha / 255.0f;
normComp = new float[numComponents];
for (int i = 0; i < numColorComponents; i++) {
normComp[i] = comp[i];
}
normComp[numColorComponents] = normAlpha;
}
} else {
comp[0] = red / fFactor;
comp[1] = green / fFactor;
comp[2] = blue / fFactor;
float rgbComp[] = cs.fromRGB(comp);
if (!hasAlpha) {
normComp = rgbComp;
} else {
float normAlpha = alpha / 255.0f;
normComp = new float[numComponents];
for (int i = 0; i < numColorComponents; i++) {
normComp[i] = rgbComp[i];
}
normComp[numColorComponents] = normAlpha;
}
}
int pxl = 0;
if (hasAlpha) {
float normAlpha = normComp[numColorComponents];
alpha = (int)(normAlpha * maxValues[numColorComponents] + 0.5f);
if (isAlphaPremultiplied) {
red = (int)(normComp[0] * normAlpha * maxValues[0] + 0.5f);
green = (int)(normComp[1] * normAlpha * maxValues[1] + 0.5f);
blue = (int)(normComp[2] * normAlpha * maxValues[2] + 0.5f);
} else {
red = (int)(normComp[0] * maxValues[0] + 0.5f);
green = (int)(normComp[1] * maxValues[1] + 0.5f);
blue = (int)(normComp[2] * maxValues[2] + 0.5f);
}
pxl = (alpha << offsets[3]) & componentMasks[3];
} else {
red = (int)(normComp[0] * maxValues[0] + 0.5f);
green = (int)(normComp[1] * maxValues[1] + 0.5f);
blue = (int)(normComp[2] * maxValues[2] + 0.5f);
}
pxl |= ((red << offsets[0]) & componentMasks[0])
| ((green << offsets[1]) & componentMasks[1])
| ((blue << offsets[2]) & componentMasks[2]);
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[];
if (pixel == null) {
ba = new byte[1];
} else {
ba = (byte[])pixel;
}
ba[0] = (byte)pxl;
return ba;
case DataBuffer.TYPE_USHORT:
short sa[];
if (pixel == null) {
sa = new short[1];
} else {
sa = (short[])pixel;
}
sa[0] = (short)pxl;
return sa;
case DataBuffer.TYPE_INT:
int ia[];
if (pixel == null) {
ia = new int[1];
} else {
ia = (int[])pixel;
}
ia[0] = pxl;
return ia;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
}
@Override
public final ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) {
if (!hasAlpha || this.isAlphaPremultiplied == isAlphaPremultiplied) {
return this;
}
int minX = raster.getMinX();
int minY = raster.getMinY();
int w = raster.getWidth();
int h = raster.getHeight();
int components[] = null;
int transparentComponents[] = new int[numComponents];
float alphaFactor = maxValues[numColorComponents];
if (isAlphaPremultiplied) {
switch (transferType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_INT:
for (int i = 0; i < h; i++, minY++) {
for (int j = 0, x = minX; j < w; j++, x++) {
components = raster.getPixel(x, minY, components);
if (components[numColorComponents] == 0) {
raster.setPixel(x, minY, transparentComponents);
} else {
float alpha = components[numColorComponents] / alphaFactor;
for (int n = 0; n < numColorComponents; n++) {
components[n] = (int)(alpha * components[n] + 0.5f);
}
raster.setPixel(x, minY, components);
}
}
}
break;
default:
// awt.214=This Color Model doesn't support this
// transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
} else {
switch (transferType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_INT:
for (int i = 0; i < h; i++, minY++) {
for (int j = 0, x = minX; j < w; j++, x++) {
components = raster.getPixel(x, minY, components);
if (components[numColorComponents] != 0) {
float alpha = alphaFactor / components[numColorComponents];
for (int n = 0; n < numColorComponents; n++) {
components[n] = (int)(alpha * components[n] + 0.5f);
}
raster.setPixel(x, minY, components);
}
}
}
break;
default:
// awt.214=This Color Model doesn't support this
// transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
}
return new DirectColorModel(cs, pixel_bits, componentMasks[0], componentMasks[1],
componentMasks[2], componentMasks[3], isAlphaPremultiplied, transferType);
}
@Override
public String toString() {
// The output format based on 1.5 release behaviour.
// It could be reveled such way:
// BufferedImage bi = new BufferedImage(1, 1,
// BufferedImage.TYPE_INT_ARGB);
// ColorModel cm = bi.getColorModel();
// System.out.println(cm.toString());
String str = "DirectColorModel:" + " rmask = " + //$NON-NLS-1$ //$NON-NLS-2$
Integer.toHexString(componentMasks[0]) + " gmask = " + //$NON-NLS-1$
Integer.toHexString(componentMasks[1]) + " bmask = " + //$NON-NLS-1$
Integer.toHexString(componentMasks[2]) + " amask = " + //$NON-NLS-1$
(!hasAlpha ? "0" : Integer.toHexString(componentMasks[3])); //$NON-NLS-1$
return str;
}
@Override
public final int[] getComponents(Object pixel, int components[], int offset) {
if (components == null) {
components = new int[numComponents + offset];
}
int intPixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])pixel;
intPixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])pixel;
intPixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])pixel;
intPixel = ia[0];
break;
default:
// awt.22D=This transferType ( {0} ) is not supported by this
// color model
throw new UnsupportedOperationException(Messages.getString("awt.22D", //$NON-NLS-1$
transferType));
}
return getComponents(intPixel, components, offset);
}
@Override
public int getRed(Object inData) {
int pixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])inData;
pixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])inData;
pixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])inData;
pixel = ia[0];
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return getRed(pixel);
}
@Override
public int getRGB(Object inData) {
int pixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])inData;
pixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])inData;
pixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])inData;
pixel = ia[0];
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return getRGB(pixel);
}
@Override
public int getGreen(Object inData) {
int pixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])inData;
pixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])inData;
pixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])inData;
pixel = ia[0];
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return getGreen(pixel);
}
@Override
public int getBlue(Object inData) {
int pixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])inData;
pixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])inData;
pixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])inData;
pixel = ia[0];
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return getBlue(pixel);
}
@Override
public int getAlpha(Object inData) {
int pixel = 0;
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte ba[] = (byte[])inData;
pixel = ba[0] & 0xff;
break;
case DataBuffer.TYPE_USHORT:
short sa[] = (short[])inData;
pixel = sa[0] & 0xffff;
break;
case DataBuffer.TYPE_INT:
int ia[] = (int[])inData;
pixel = ia[0];
break;
default:
// awt.214=This Color Model doesn't support this transferType
throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$
}
return getAlpha(pixel);
}
@Override
public final WritableRaster createCompatibleWritableRaster(int w, int h) {
if (w <= 0 || h <= 0) {
// awt.22E=w or h is less than or equal to zero
throw new IllegalArgumentException(Messages.getString("awt.22E")); //$NON-NLS-1$
}
int bandMasks[] = componentMasks.clone();
if (pixel_bits > 16) {
return Raster.createPackedRaster(DataBuffer.TYPE_INT, w, h, bandMasks, null);
} else if (pixel_bits > 8) {
return Raster.createPackedRaster(DataBuffer.TYPE_USHORT, w, h, bandMasks, null);
} else {
return Raster.createPackedRaster(DataBuffer.TYPE_BYTE, w, h, bandMasks, null);
}
}
@Override
public boolean isCompatibleRaster(Raster raster) {
SampleModel sm = raster.getSampleModel();
if (!(sm instanceof SinglePixelPackedSampleModel)) {
return false;
}
SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sm;
if (sppsm.getNumBands() != numComponents) {
return false;
}
if (raster.getTransferType() != transferType) {
return false;
}
int maskBands[] = sppsm.getBitMasks();
return Arrays.equals(maskBands, componentMasks);
}
@Override
public int getDataElement(int components[], int offset) {
int pixel = 0;
for (int i = 0; i < numComponents; i++) {
pixel |= (components[offset + i] << offsets[i]) & componentMasks[i];
}
return pixel;
}
@Override
public final int[] getComponents(int pixel, int components[], int offset) {
if (components == null) {
components = new int[numComponents + offset];
}
for (int i = 0; i < numComponents; i++) {
components[offset + i] = (pixel & componentMasks[i]) >> offsets[i];
}
return components;
}
@Override
public final int getRed(int pixel) {
if (is_sRGB) {
return getComponentFrom_sRGB(pixel, 0);
}
if (is_LINEAR_RGB) {
return getComponentFrom_LINEAR_RGB(pixel, 0);
}
return getComponentFrom_RGB(pixel, 0);
}
@Override
public final int getRGB(int pixel) {
return (getAlpha(pixel) << 24) | (getRed(pixel) << 16) | (getGreen(pixel) << 8)
| getBlue(pixel);
}
@Override
public final int getGreen(int pixel) {
if (is_sRGB) {
return getComponentFrom_sRGB(pixel, 1);
}
if (is_LINEAR_RGB) {
return getComponentFrom_LINEAR_RGB(pixel, 1);
}
return getComponentFrom_RGB(pixel, 1);
}
@Override
public final int getBlue(int pixel) {
if (is_sRGB) {
return getComponentFrom_sRGB(pixel, 2);
}
if (is_LINEAR_RGB) {
return getComponentFrom_LINEAR_RGB(pixel, 2);
}
return getComponentFrom_RGB(pixel, 2);
}
@Override
public final int getAlpha(int pixel) {
if (!hasAlpha) {
return 255;
}
int a = (pixel & componentMasks[3]) >>> offsets[3];
if (bits[3] == 8) {
return a;
}
return alphaLUT[a] & 0xff;
}
/**
* Gets the red mask.
*
* @return the red mask.
*/
public final int getRedMask() {
return componentMasks[0];
}
/**
* Gets the green mask.
*
* @return the green mask.
*/
public final int getGreenMask() {
return componentMasks[1];
}
/**
* Gets the blue mask.
*
* @return the blue mask.
*/
public final int getBlueMask() {
return componentMasks[2];
}
/**
* Gets the alpha mask.
*
* @return the alpha mask.
*/
public final int getAlphaMask() {
if (hasAlpha) {
return componentMasks[3];
}
return 0;
}
/**
* Initialization of Lookup tables.
*/
private void initLUTs() {
is_sRGB = cs.isCS_sRGB();
is_LINEAR_RGB = (cs == LUTColorConverter.LINEAR_RGB_CS);
if (is_LINEAR_RGB) {
if (maxBitLength > 8) {
LINEAR_RGB_Length = 16;
from_LINEAR_RGB_LUT = LUTColorConverter.getFrom16lRGBtosRGB_LUT();
to_LINEAR_16RGB_LUT = LUTColorConverter.getFromsRGBto16lRGB_LUT();
} else {
LINEAR_RGB_Length = 8;
from_LINEAR_RGB_LUT = LUTColorConverter.getFrom8lRGBtosRGB_LUT();
to_LINEAR_8RGB_LUT = LUTColorConverter.getFromsRGBto8lRGB_LUT();
}
fFactor = ((1 << LINEAR_RGB_Length) - 1);
} else {
fFactor = 255.0f;
}
if (hasAlpha && bits[3] != 8) {
alphaLUT = new byte[maxValues[3] + 1];
for (int i = 0; i <= maxValues[3]; i++) {
alphaLUT[i] = (byte)(scales[3] * i + 0.5f);
}
}
if (!isAlphaPremultiplied) {
colorLUTs = new byte[3][];
if (is_sRGB) {
for (int i = 0; i < numColorComponents; i++) {
if (bits[i] != 8) {
for (int j = 0; j < i; j++) {
if (bits[i] == bits[j]) {
colorLUTs[i] = colorLUTs[j];
break;
}
}
colorLUTs[i] = new byte[maxValues[i] + 1];
for (int j = 0; j <= maxValues[i]; j++) {
colorLUTs[i][j] = (byte)(scales[i] * j + 0.5f);
}
}
}
}
if (is_LINEAR_RGB) {
for (int i = 0; i < numColorComponents; i++) {
if (bits[i] != LINEAR_RGB_Length) {
for (int j = 0; j < i; j++) {
if (bits[i] == bits[j]) {
colorLUTs[i] = colorLUTs[j];
break;
}
}
colorLUTs[i] = new byte[maxValues[i] + 1];
for (int j = 0; j <= maxValues[0]; j++) {
int idx;
if (LINEAR_RGB_Length == 8) {
idx = (int)(scales[i] * j + 0.5f);
} else {
idx = (int)(scales[i] * j * 257.0f + 0.5f);
}
colorLUTs[i][j] = from_LINEAR_RGB_LUT[idx];
}
}
}
}
}
}
/**
* This method return RGB component value if Color Model has sRGB
* ColorSpace.
*
* @param pixel
* the integer representation of the pixel.
* @param idx
* the index of the pixel component.
* @return the value of the pixel component scaled from 0 to 255.
*/
private int getComponentFrom_sRGB(int pixel, int idx) {
int comp = (pixel & componentMasks[idx]) >> offsets[idx];
if (isAlphaPremultiplied) {
int alpha = (pixel & componentMasks[3]) >>> offsets[3];
comp = alpha == 0 ? 0 : (int)(scales[idx] * comp * 255.0f / (scales[3] * alpha) + 0.5f);
} else if (bits[idx] != 8) {
comp = colorLUTs[idx][comp] & 0xff;
}
return comp;
}
/**
* This method return RGB component value if Color Model has Linear RGB
* ColorSpace.
*
* @param pixel
* the integer representation of the pixel.
* @param idx
* the index of the pixel component.
* @return the value of the pixel component scaled from 0 to 255.
*/
private int getComponentFrom_LINEAR_RGB(int pixel, int idx) {
int comp = (pixel & componentMasks[idx]) >> offsets[idx];
if (isAlphaPremultiplied) {
float factor = ((1 << LINEAR_RGB_Length) - 1);
int alpha = (pixel & componentMasks[3]) >> offsets[3];
comp = alpha == 0 ? 0 : (int)(scales[idx] * comp * factor / (scales[3] * alpha) + 0.5f);
} else if (bits[idx] != LINEAR_RGB_Length) {
comp = colorLUTs[idx][comp] & 0xff;
} else {
comp = from_LINEAR_RGB_LUT[comp] & 0xff;
}
return comp;
}
/**
* This method return RGB component value if Color Model has arbitrary RGB
* ColorSapce.
*
* @param pixel
* the integer representation of the pixel.
* @param idx
* the index of the pixel component.
* @return the value of the pixel component scaled from 0 to 255.
*/
private int getComponentFrom_RGB(int pixel, int idx) {
int components[] = getComponents(pixel, null, 0);
float[] normComponents = getNormalizedComponents(components, 0, null, 0);
float[] sRGBcomponents = cs.toRGB(normComponents);
return (int)(sRGBcomponents[idx] * 255.0f + 0.5f);
}
}