/* * 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); } }