1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Oleg V. Khaschansky 19 * @version $Revision$ 20 */ 21package org.apache.harmony.awt.gl.color; 22 23import java.awt.color.ColorSpace; 24import java.awt.image.BufferedImage; 25import java.awt.image.ColorModel; 26import java.awt.image.Raster; 27import java.awt.image.WritableRaster; 28 29/** 30 * This class combines ColorScaler, ICC_Transform and NativeImageFormat functionality 31 * in the workflows for different types of input/output pixel data. 32 */ 33public class ColorConverter { 34 private ColorScaler scaler = new ColorScaler(); 35 36 public void loadScalingData(ColorSpace cs) { 37 scaler.loadScalingData(cs); 38 } 39 40 /** 41 * Translates pixels, stored in source buffered image and writes the data 42 * to the destination image. 43 * @param t - ICC transform 44 * @param src - source image 45 * @param dst - destination image 46 */ 47 public void translateColor(ICC_Transform t, 48 BufferedImage src, BufferedImage dst) { 49 NativeImageFormat srcIF = NativeImageFormat.createNativeImageFormat(src); 50 NativeImageFormat dstIF = NativeImageFormat.createNativeImageFormat(dst); 51 52 if (srcIF != null && dstIF != null) { 53 t.translateColors(srcIF, dstIF); 54 return; 55 } 56 57 srcIF = createImageFormat(src); 58 dstIF = createImageFormat(dst); 59 60 short srcChanData[] = (short[]) srcIF.getChannelData(); 61 short dstChanData[] = (short[]) dstIF.getChannelData(); 62 63 ColorModel srcCM = src.getColorModel(); 64 int nColorChannels = srcCM.getNumColorComponents(); 65 scaler.loadScalingData(srcCM.getColorSpace()); // input scaling data 66 ColorModel dstCM = dst.getColorModel(); 67 68 // Prepare array for alpha channel 69 float alpha[] = null; 70 boolean saveAlpha = srcCM.hasAlpha() && dstCM.hasAlpha(); 71 if (saveAlpha) { 72 alpha = new float[src.getWidth()*src.getHeight()]; 73 } 74 75 WritableRaster wr = src.getRaster(); 76 int srcDataPos = 0, alphaPos = 0; 77 float normalizedVal[]; 78 for (int row=0, nRows = srcIF.getNumRows(); row<nRows; row++) { 79 for (int col=0, nCols = srcIF.getNumCols(); col<nCols; col++) { 80 normalizedVal = srcCM.getNormalizedComponents( 81 wr.getDataElements(col, row, null), 82 null, 0); 83 // Save alpha channel 84 if (saveAlpha) { 85 // We need nColorChannels'th element cause it's nChannels - 1 86 alpha[alphaPos++] = normalizedVal[nColorChannels]; 87 } 88 scaler.scale(normalizedVal, srcChanData, srcDataPos); 89 srcDataPos += nColorChannels; 90 } 91 } 92 93 t.translateColors(srcIF, dstIF); 94 95 nColorChannels = dstCM.getNumColorComponents(); 96 boolean fillAlpha = dstCM.hasAlpha(); 97 scaler.loadScalingData(dstCM.getColorSpace()); // output scaling data 98 float dstPixel[] = new float[dstCM.getNumComponents()]; 99 int dstDataPos = 0; 100 alphaPos = 0; 101 wr = dst.getRaster(); 102 103 for (int row=0, nRows = dstIF.getNumRows(); row<nRows; row++) { 104 for (int col=0, nCols = dstIF.getNumCols(); col<nCols; col++) { 105 scaler.unscale(dstPixel, dstChanData, dstDataPos); 106 dstDataPos += nColorChannels; 107 if (fillAlpha) { 108 if (saveAlpha) { 109 dstPixel[nColorChannels] = alpha[alphaPos++]; 110 } else { 111 dstPixel[nColorChannels] = 1f; 112 } 113 } 114 wr.setDataElements(col, row, 115 dstCM.getDataElements(dstPixel, 0 , null)); 116 } 117 } 118 } 119 120 /** 121 * Translates pixels, stored in the float data buffer. 122 * Each pixel occupies separate array. Input pixels passed in the buffer 123 * are replaced by output pixels and then the buffer is returned 124 * @param t - ICC transform 125 * @param buffer - data buffer 126 * @param srcCS - source color space 127 * @param dstCS - destination color space 128 * @param nPixels - number of pixels 129 * @return translated pixels 130 */ 131 public float[][] translateColor(ICC_Transform t, 132 float buffer[][], 133 ColorSpace srcCS, 134 ColorSpace dstCS, 135 int nPixels) { 136 // Scale source data 137 if (srcCS != null) { // if it is null use old scaling data 138 scaler.loadScalingData(srcCS); 139 } 140 int nSrcChannels = t.getNumInputChannels(); 141 short srcShortData[] = new short[nPixels*nSrcChannels]; 142 for (int i=0, srcDataPos = 0; i<nPixels; i++) { 143 scaler.scale(buffer[i], srcShortData, srcDataPos); 144 srcDataPos += nSrcChannels; 145 } 146 147 // Apply transform 148 short dstShortData[] = this.translateColor(t, srcShortData, null); 149 150 int nDstChannels = t.getNumOutputChannels(); 151 int bufferSize = buffer[0].length; 152 if (bufferSize < nDstChannels + 1) { // Re-allocate buffer if needed 153 for (int i=0; i<nPixels; i++) { 154 // One extra element reserved for alpha 155 buffer[i] = new float[nDstChannels + 1]; 156 } 157 } 158 159 // Unscale destination data 160 if (dstCS != null) { // if it is null use old scaling data 161 scaler.loadScalingData(dstCS); 162 } 163 for (int i=0, dstDataPos = 0; i<nPixels; i++) { 164 scaler.unscale(buffer[i], dstShortData, dstDataPos); 165 dstDataPos += nDstChannels; 166 } 167 168 return buffer; 169 } 170 171 /** 172 * Translates pixels stored in a raster. 173 * All data types are supported 174 * @param t - ICC transform 175 * @param src - source pixels 176 * @param dst - destination pixels 177 */ 178 public void translateColor(ICC_Transform t, Raster src, WritableRaster dst) { 179 try{ 180 NativeImageFormat srcFmt = NativeImageFormat.createNativeImageFormat(src); 181 NativeImageFormat dstFmt = NativeImageFormat.createNativeImageFormat(dst); 182 183 if (srcFmt != null && dstFmt != null) { 184 t.translateColors(srcFmt, dstFmt); 185 return; 186 } 187 } catch (IllegalArgumentException e) { 188 } 189 190 // Go ahead and rescale the source image 191 scaler.loadScalingData(src, t.getSrc()); 192 short srcData[] = scaler.scale(src); 193 194 short dstData[] = translateColor(t, srcData, null); 195 196 scaler.loadScalingData(dst, t.getDst()); 197 scaler.unscale(dstData, dst); 198 } 199 200 /** 201 * Translates pixels stored in an array of shorts. 202 * Samples are stored one-by-one, i.e. array structure is like following: RGBRGBRGB... 203 * The number of pixels is (size of the array) / (number of components). 204 * @param t - ICC transform 205 * @param src - source pixels 206 * @param dst - destination pixels 207 * @return destination pixels, stored in the array, passed in dst 208 */ 209 public short[] translateColor(ICC_Transform t, short src[], short dst[]) { 210 NativeImageFormat srcFmt = createImageFormat(t, src, 0, true); 211 NativeImageFormat dstFmt = createImageFormat(t, dst, srcFmt.getNumCols(), false); 212 213 t.translateColors(srcFmt, dstFmt); 214 215 return (short[]) dstFmt.getChannelData(); 216 } 217 218 219 /** 220 * Creates NativeImageFormat from buffered image. 221 * @param bi - buffered image 222 * @return created NativeImageFormat 223 */ 224 private NativeImageFormat createImageFormat(BufferedImage bi) { 225 int nRows = bi.getHeight(); 226 int nCols = bi.getWidth(); 227 int nComps = bi.getColorModel().getNumColorComponents(); 228 short imgData[] = new short[nRows*nCols*nComps]; 229 return new NativeImageFormat( 230 imgData, nComps, nRows, nCols); 231 } 232 233 /** 234 * Creates one-row NativeImageFormat, using either nCols if it is positive, 235 * or arr.length to determine the number of pixels 236 * 237 * @param t - transform 238 * @param arr - short array or null if nCols is positive 239 * @param nCols - number of pixels in the array or 0 if array is not null 240 * @param in - is it an input or output array 241 * @return one-row NativeImageFormat 242 */ 243 private NativeImageFormat createImageFormat( 244 ICC_Transform t, short arr[], int nCols, boolean in 245 ) { 246 int nComponents = in ? t.getNumInputChannels() : t.getNumOutputChannels(); 247 248 if (arr == null || arr.length < nCols*nComponents) { 249 arr = new short[nCols*nComponents]; 250 } 251 252 if (nCols == 0) 253 nCols = arr.length / nComponents; 254 255 return new NativeImageFormat(arr, nComponents, 1, nCols); 256 } 257} 258