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.color.ICC_Profile; 25import java.awt.image.DataBuffer; 26import java.awt.image.Raster; 27import java.awt.image.SampleModel; 28import java.awt.image.WritableRaster; 29 30/** 31 * This class provides functionality for scaling color data when 32 * ranges of the source and destination color values differs. 33 */ 34public class ColorScaler { 35 private static final float MAX_SHORT = 0xFFFF; 36 private static final float MAX_SIGNED_SHORT = 0x7FFF; 37 38 private static final float MAX_XYZ = 1f + (32767f/32768f); 39 40 // Cached values for scaling color data 41 private float[] channelMinValues = null; 42 private float[] channelMulipliers = null; // for scale 43 private float[] invChannelMulipliers = null; // for unscale 44 45 int nColorChannels = 0; 46 47 // For scaling rasters, false if transfer type is double or float 48 boolean isTTypeIntegral = false; 49 50 /** 51 * Loads scaling data for raster. Note, if profile pf is null, 52 * for non-integral data types multipliers are not initialized. 53 * @param r - raster 54 * @param pf - profile which helps to determine the ranges of the color data 55 */ 56 public void loadScalingData(Raster r, ICC_Profile pf) { 57 boolean isSrcTTypeIntegral = 58 r.getTransferType() != DataBuffer.TYPE_FLOAT && 59 r.getTransferType() != DataBuffer.TYPE_DOUBLE; 60 if (isSrcTTypeIntegral) 61 loadScalingData(r.getSampleModel()); 62 else if (pf != null) 63 loadScalingData(pf); 64 } 65 66 /** 67 * Use this method only for integral transfer types. 68 * Extracts min/max values from the sample model 69 * @param sm - sample model 70 */ 71 public void loadScalingData(SampleModel sm) { 72 // Supposing integral transfer type 73 isTTypeIntegral = true; 74 75 nColorChannels = sm.getNumBands(); 76 77 channelMinValues = new float[nColorChannels]; 78 channelMulipliers = new float[nColorChannels]; 79 invChannelMulipliers = new float[nColorChannels]; 80 81 boolean isSignedShort = 82 (sm.getTransferType() == DataBuffer.TYPE_SHORT); 83 84 float maxVal; 85 for (int i=0; i<nColorChannels; i++) { 86 channelMinValues[i] = 0; 87 if (isSignedShort) { 88 channelMulipliers[i] = MAX_SHORT / MAX_SIGNED_SHORT; 89 invChannelMulipliers[i] = MAX_SIGNED_SHORT / MAX_SHORT; 90 } else { 91 maxVal = ((1 << sm.getSampleSize(i)) - 1); 92 channelMulipliers[i] = MAX_SHORT / maxVal; 93 invChannelMulipliers[i] = maxVal / MAX_SHORT; 94 } 95 } 96 } 97 98 /** 99 * Use this method only for double of float transfer types. 100 * Extracts scaling data from the color space signature 101 * and other tags, stored in the profile 102 * @param pf - ICC profile 103 */ 104 public void loadScalingData(ICC_Profile pf) { 105 // Supposing double or float transfer type 106 isTTypeIntegral = false; 107 108 nColorChannels = pf.getNumComponents(); 109 110 // Get min/max values directly from the profile 111 // Very much like fillMinMaxValues in ICC_ColorSpace 112 float maxValues[] = new float[nColorChannels]; 113 float minValues[] = new float[nColorChannels]; 114 115 switch (pf.getColorSpaceType()) { 116 case ColorSpace.TYPE_XYZ: 117 minValues[0] = 0; 118 minValues[1] = 0; 119 minValues[2] = 0; 120 maxValues[0] = MAX_XYZ; 121 maxValues[1] = MAX_XYZ; 122 maxValues[2] = MAX_XYZ; 123 break; 124 case ColorSpace.TYPE_Lab: 125 minValues[0] = 0; 126 minValues[1] = -128; 127 minValues[2] = -128; 128 maxValues[0] = 100; 129 maxValues[1] = 127; 130 maxValues[2] = 127; 131 break; 132 default: 133 for (int i=0; i<nColorChannels; i++) { 134 minValues[i] = 0; 135 maxValues[i] = 1; 136 } 137 } 138 139 channelMinValues = minValues; 140 channelMulipliers = new float[nColorChannels]; 141 invChannelMulipliers = new float[nColorChannels]; 142 143 for (int i = 0; i < nColorChannels; i++) { 144 channelMulipliers[i] = 145 MAX_SHORT / (maxValues[i] - channelMinValues[i]); 146 147 invChannelMulipliers[i] = 148 (maxValues[i] - channelMinValues[i]) / MAX_SHORT; 149 } 150 } 151 152 /** 153 * Extracts scaling data from the color space 154 * @param cs - color space 155 */ 156 public void loadScalingData(ColorSpace cs) { 157 nColorChannels = cs.getNumComponents(); 158 159 channelMinValues = new float[nColorChannels]; 160 channelMulipliers = new float[nColorChannels]; 161 invChannelMulipliers = new float[nColorChannels]; 162 163 for (int i = 0; i < nColorChannels; i++) { 164 channelMinValues[i] = cs.getMinValue(i); 165 166 channelMulipliers[i] = 167 MAX_SHORT / (cs.getMaxValue(i) - channelMinValues[i]); 168 169 invChannelMulipliers[i] = 170 (cs.getMaxValue(i) - channelMinValues[i]) / MAX_SHORT; 171 } 172 } 173 174 /** 175 * Scales and normalizes the whole raster and returns the result 176 * in the float array 177 * @param r - source raster 178 * @return scaled and normalized raster data 179 */ 180 public float[][] scaleNormalize(Raster r) { 181 int width = r.getWidth(); 182 int height = r.getHeight(); 183 float result[][] = new float[width*height][nColorChannels]; 184 float normMultipliers[] = new float[nColorChannels]; 185 186 int pos = 0; 187 if (isTTypeIntegral) { 188 // Change max value from MAX_SHORT to 1f 189 for (int i=0; i<nColorChannels; i++) { 190 normMultipliers[i] = channelMulipliers[i] / MAX_SHORT; 191 } 192 193 int sample; 194 for (int row=r.getMinX(); row<width; row++) { 195 for (int col=r.getMinY(); col<height; col++) { 196 for (int chan = 0; chan < nColorChannels; chan++) { 197 sample = r.getSample(row, col, chan); 198 result[pos][chan] = (sample * normMultipliers[chan]); 199 } 200 pos++; 201 } 202 } 203 } else { // Just get the samples... 204 for (int row=r.getMinX(); row<width; row++) { 205 for (int col=r.getMinY(); col<height; col++) { 206 for (int chan = 0; chan < nColorChannels; chan++) { 207 result[pos][chan] = r.getSampleFloat(row, col, chan); 208 } 209 pos++; 210 } 211 } 212 } 213 return result; 214 } 215 216 /** 217 * Unscale the whole float array and put the result 218 * in the raster 219 * @param r - destination raster 220 * @param data - input pixels 221 */ 222 public void unscaleNormalized(WritableRaster r, float data[][]) { 223 int width = r.getWidth(); 224 int height = r.getHeight(); 225 float normMultipliers[] = new float[nColorChannels]; 226 227 int pos = 0; 228 if (isTTypeIntegral) { 229 // Change max value from MAX_SHORT to 1f 230 for (int i=0; i<nColorChannels; i++) { 231 normMultipliers[i] = invChannelMulipliers[i] * MAX_SHORT; 232 } 233 234 int sample; 235 for (int row=r.getMinX(); row<width; row++) { 236 for (int col=r.getMinY(); col<height; col++) { 237 for (int chan = 0; chan < nColorChannels; chan++) { 238 sample = (int) (data[pos][chan] * normMultipliers[chan] + 0.5f); 239 r.setSample(row, col, chan, sample); 240 } 241 pos++; 242 } 243 } 244 } else { // Just set the samples... 245 for (int row=r.getMinX(); row<width; row++) { 246 for (int col=r.getMinY(); col<height; col++) { 247 for (int chan = 0; chan < nColorChannels; chan++) { 248 r.setSample(row, col, chan, data[pos][chan]); 249 } 250 pos++; 251 } 252 } 253 } 254 } 255 256 /** 257 * Scales the whole raster to short and returns the result 258 * in the array 259 * @param r - source raster 260 * @return scaled and normalized raster data 261 */ 262 public short[] scale(Raster r) { 263 int width = r.getWidth(); 264 int height = r.getHeight(); 265 short result[] = new short[width*height*nColorChannels]; 266 267 int pos = 0; 268 if (isTTypeIntegral) { 269 int sample; 270 for (int row=r.getMinX(); row<width; row++) { 271 for (int col=r.getMinY(); col<height; col++) { 272 for (int chan = 0; chan < nColorChannels; chan++) { 273 sample = r.getSample(row, col, chan); 274 result[pos++] = 275 (short) (sample * channelMulipliers[chan] + 0.5f); 276 } 277 } 278 } 279 } else { 280 float sample; 281 for (int row=r.getMinX(); row<width; row++) { 282 for (int col=r.getMinY(); col<height; col++) { 283 for (int chan = 0; chan < nColorChannels; chan++) { 284 sample = r.getSampleFloat(row, col, chan); 285 result[pos++] = (short) ((sample - channelMinValues[chan]) 286 * channelMulipliers[chan] + 0.5f); 287 } 288 } 289 } 290 } 291 return result; 292 } 293 294 /** 295 * Unscales the whole data array and puts obtained values to the raster 296 * @param data - input data 297 * @param wr - destination raster 298 */ 299 public void unscale(short[] data, WritableRaster wr) { 300 int width = wr.getWidth(); 301 int height = wr.getHeight(); 302 303 int pos = 0; 304 if (isTTypeIntegral) { 305 int sample; 306 for (int row=wr.getMinX(); row<width; row++) { 307 for (int col=wr.getMinY(); col<height; col++) { 308 for (int chan = 0; chan < nColorChannels; chan++) { 309 sample = (int) ((data[pos++] & 0xFFFF) * 310 invChannelMulipliers[chan] + 0.5f); 311 wr.setSample(row, col, chan, sample); 312 } 313 } 314 } 315 } else { 316 float sample; 317 for (int row=wr.getMinX(); row<width; row++) { 318 for (int col=wr.getMinY(); col<height; col++) { 319 for (int chan = 0; chan < nColorChannels; chan++) { 320 sample = (data[pos++] & 0xFFFF) * 321 invChannelMulipliers[chan] + channelMinValues[chan]; 322 wr.setSample(row, col, chan, sample); 323 } 324 } 325 } 326 } 327 } 328 329 /** 330 * Scales one pixel and puts obtained values to the chanData 331 * @param pixelData - input pixel 332 * @param chanData - output buffer 333 * @param chanDataOffset - output buffer offset 334 */ 335 public void scale(float[] pixelData, short[] chanData, int chanDataOffset) { 336 for (int chan = 0; chan < nColorChannels; chan++) { 337 chanData[chanDataOffset + chan] = 338 (short) ((pixelData[chan] - channelMinValues[chan]) * 339 channelMulipliers[chan] + 0.5f); 340 } 341 } 342 343 /** 344 * Unscales one pixel and puts obtained values to the pixelData 345 * @param pixelData - output pixel 346 * @param chanData - input buffer 347 * @param chanDataOffset - input buffer offset 348 */ 349 public void unscale(float[] pixelData, short[] chanData, int chanDataOffset) { 350 for (int chan = 0; chan < nColorChannels; chan++) { 351 pixelData[chan] = (chanData[chanDataOffset + chan] & 0xFFFF) 352 * invChannelMulipliers[chan] + channelMinValues[chan]; 353 } 354 } 355}