001 /* ComponentColorModel.java -- 002 Copyright (C) 2000, 2002, 2004 Free Software Foundation 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.awt.image; 040 041 import gnu.java.awt.Buffers; 042 043 import java.awt.Point; 044 import java.awt.color.ColorSpace; 045 import java.util.Arrays; 046 047 public class ComponentColorModel extends ColorModel 048 { 049 // Find sum of all elements of the array. 050 private static int sum(int[] values) 051 { 052 int sum = 0; 053 for (int i=0; i<values.length; i++) 054 sum += values[i]; 055 return sum; 056 } 057 058 // Create an appropriate array of bits, given a colorspace (ie, number of 059 // bands), size of the storage data type, and presence of an alpha band. 060 private static int[] findBits(ColorSpace colorSpace, int transferType, 061 boolean hasAlpha) 062 { 063 int[] bits; 064 if (hasAlpha) 065 bits = new int[colorSpace.getNumComponents()+1]; 066 else 067 bits = new int[colorSpace.getNumComponents()]; 068 069 Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType)); 070 071 return bits; 072 } 073 074 public ComponentColorModel(ColorSpace colorSpace, int[] bits, 075 boolean hasAlpha, 076 boolean isAlphaPremultiplied, 077 int transparency, int transferType) 078 { 079 super(sum(bits), bits, colorSpace, hasAlpha, isAlphaPremultiplied, 080 transparency, transferType); 081 } 082 083 /** 084 * Construct a new ComponentColorModel. 085 * 086 * This constructor makes all bits of each sample significant, so for a 087 * transferType of DataBuffer.BYTE, the bits per sample is 8, etc. If 088 * both hasAlpha and isAlphaPremultiplied are true, color samples are 089 * assumed to be premultiplied by the alpha component. Transparency may be 090 * one of OPAQUE, BITMASK, or TRANSLUCENT. 091 * 092 * @param colorSpace The colorspace for this color model. 093 * @param hasAlpha True if there is an alpha component. 094 * @param isAlphaPremultiplied True if colors are already multiplied by 095 * alpha. 096 * @param transparency The type of alpha values. 097 * @param transferType Data type of pixel sample values. 098 * @since 1.4 099 */ 100 public ComponentColorModel(ColorSpace colorSpace, 101 boolean hasAlpha, 102 boolean isAlphaPremultiplied, 103 int transparency, int transferType) 104 { 105 this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha, 106 isAlphaPremultiplied, transparency, transferType); 107 } 108 109 public int getRed(int pixel) 110 { 111 if (getNumComponents()>1) throw new IllegalArgumentException(); 112 return (int) getRGBFloat(pixel)[0]; 113 } 114 115 public int getGreen(int pixel) 116 { 117 if (getNumComponents()>1) throw new IllegalArgumentException(); 118 return (int) getRGBFloat(pixel)[0]; 119 } 120 121 public int getBlue(int pixel) 122 { 123 if (getNumComponents()>1) throw new IllegalArgumentException(); 124 return (int) getRGBFloat(pixel)[0]; 125 } 126 127 public int getAlpha(int pixel) 128 { 129 if (getNumComponents()>1) throw new IllegalArgumentException(); 130 int shift = 8 - getComponentSize(getNumColorComponents()); 131 if (shift >= 0) return pixel << shift; 132 return pixel >> (-shift); 133 } 134 135 public int getRGB(int pixel) 136 { 137 float[] rgb = getRGBFloat(pixel); 138 int ret = getRGB(rgb); 139 if (hasAlpha()) ret |= getAlpha(pixel) << 24; 140 return ret; 141 } 142 143 144 /* Note, it's OK to pass a to large array to toRGB(). Extra 145 elements are ignored. */ 146 147 private float[] getRGBFloat(int pixel) 148 { 149 float[] data = { pixel }; 150 return cspace.toRGB(data); 151 } 152 153 private float[] getRGBFloat(Object inData) 154 { 155 DataBuffer buffer = 156 Buffers.createBufferFromData(transferType, inData, 157 getNumComponents()); 158 int colors = getNumColorComponents(); 159 float[] data = new float[colors]; 160 161 // FIXME: unpremultiply data that is premultiplied 162 for (int i=0; i<colors; i++) 163 { 164 float maxValue = (1<<getComponentSize(i))-1; 165 data[i] = buffer.getElemFloat(i)/maxValue; 166 } 167 float[] rgb = cspace.toRGB(data); 168 return rgb; 169 } 170 171 public int getRed(Object inData) 172 { 173 return (int) getRGBFloat(inData)[0]*255; 174 } 175 176 public int getGreen(Object inData) 177 { 178 return (int) getRGBFloat(inData)[1]*255; 179 } 180 181 public int getBlue(Object inData) 182 { 183 return (int) getRGBFloat(inData)[2]*255; 184 } 185 186 public int getAlpha(Object inData) 187 { 188 DataBuffer buffer = 189 Buffers.createBufferFromData(transferType, inData, 190 getNumComponents()); 191 int shift = 8 - getComponentSize(getNumColorComponents()); 192 int alpha = buffer.getElem(getNumColorComponents()); 193 if (shift >= 0) return alpha << shift; 194 return alpha >> (-shift); 195 } 196 197 private int getRGB(float[] rgb) 198 { 199 /* NOTE: We could cast to byte instead of int here. This would 200 avoid bits spilling over from one bit field to 201 another. But, if we assume that floats are in the [0.0, 202 1.0] range, this will never happen anyway. */ 203 204 /* Remember to multiply BEFORE casting to int, otherwise, decimal 205 point data will be lost. */ 206 int ret = 207 (((int) (rgb[0]*255F)) << 16) | 208 (((int) (rgb[1]*255F)) << 8) | 209 (((int) (rgb[2]*255F)) << 0); 210 return ret; 211 } 212 213 /** 214 * @param inData pixel data of transferType, as returned by the 215 * getDataElements method in SampleModel. 216 */ 217 public int getRGB(Object inData) 218 { 219 float[] rgb = getRGBFloat(inData); 220 int ret = getRGB(rgb); 221 if (hasAlpha()) ret |= getAlpha(inData) << 24; 222 return ret; 223 } 224 225 public Object getDataElements(int rgb, Object pixel) 226 { 227 // Convert rgb to [0.0, 1.0] sRGB values. 228 float[] rgbFloats = { 229 ((rgb >> 16)&0xff)/255.0F, 230 ((rgb >> 8)&0xff)/255.0F, 231 ((rgb >> 0)&0xff)/255.0F 232 }; 233 234 // Convert from rgb to color space components. 235 float[] data = cspace.fromRGB(rgbFloats); 236 DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 237 getNumComponents()); 238 int numColors = getNumColorComponents(); 239 240 if (hasAlpha()) 241 { 242 float alpha = ((rgb >> 24)&0xff)/255.0F; 243 244 /* If color model has alpha and should be premultiplied, multiply 245 color space components with alpha value. */ 246 if (isAlphaPremultiplied()) { 247 for (int i=0; i<numColors; i++) 248 data[i] *= alpha; 249 } 250 // Scale the alpha sample to the correct number of bits. 251 alpha *= (1<<(bits[numColors]-1)); 252 // Arrange the alpha sample in the output array. 253 buffer.setElemFloat(numColors, alpha); 254 } 255 for (int i=0; i<numColors; i++) 256 { 257 // Scale the color samples to the correct number of bits. 258 float value = data[i]*(1<<(bits[i]-1)); 259 // Arrange the color samples in the output array. 260 buffer.setElemFloat(i, value); 261 } 262 return Buffers.getData(buffer); 263 } 264 265 public int[] getComponents(int pixel, int[] components, int offset) 266 { 267 if (getNumComponents()>1) throw new IllegalArgumentException(); 268 if (components == null) 269 components = new int[getNumComponents() + offset]; 270 components[offset] = pixel; 271 return components; 272 } 273 274 public int[] getComponents(Object pixel, int[] components, int offset) 275 { 276 DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 277 getNumComponents()); 278 int numComponents = getNumComponents(); 279 280 if (components == null) 281 components = new int[numComponents + offset]; 282 283 for (int i=0; i<numComponents; i++) 284 components[offset++] = buffer.getElem(i); 285 286 return components; 287 } 288 289 public int getDataElement(int[] components, int offset) 290 { 291 if (getNumComponents()>1) throw new IllegalArgumentException(); 292 return components[offset]; 293 } 294 295 public Object getDataElements(int[] components, int offset, Object obj) 296 { 297 DataBuffer buffer = Buffers.createBuffer(transferType, obj, 298 getNumComponents()); 299 int numComponents = getNumComponents(); 300 301 for (int i=0; i<numComponents; i++) 302 buffer.setElem(i, components[offset++]); 303 304 return Buffers.getData(buffer); 305 } 306 307 public ColorModel coerceData(WritableRaster raster, 308 boolean isAlphaPremultiplied) { 309 if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha()) 310 return this; 311 312 /* TODO: provide better implementation based on the 313 assumptions we can make due to the specific type of the 314 color model. */ 315 super.coerceDataWorker(raster, isAlphaPremultiplied); 316 317 return new ComponentColorModel(cspace, hasAlpha, isAlphaPremultiplied, 318 transparency, transferType); 319 } 320 321 public boolean isCompatibleRaster(Raster raster) 322 { 323 return super.isCompatibleRaster(raster); 324 // FIXME: Should we test something more here? (Why override?) 325 } 326 327 public WritableRaster createCompatibleWritableRaster(int w, int h) 328 { 329 SampleModel sm = createCompatibleSampleModel(w, h); 330 Point origin = new Point(0, 0); 331 return Raster.createWritableRaster(sm, origin); 332 } 333 334 335 /** 336 * Creates a <code>SampleModel</code> whose arrangement of pixel 337 * data is compatible to this <code>ColorModel</code>. 338 * 339 * @param w the number of pixels in the horizontal direction. 340 * @param h the number of pixels in the vertical direction. 341 */ 342 public SampleModel createCompatibleSampleModel(int w, int h) 343 { 344 int pixelStride, scanlineStride; 345 int[] bandOffsets; 346 347 pixelStride = getNumComponents(); 348 scanlineStride = pixelStride * w; 349 350 /* We might be able to re-use the same bandOffsets array among 351 * multiple calls to this method. However, this optimization does 352 * not seem worthwile because setting up descriptive data 353 * structures (such as SampleModels) is neglectible in comparision 354 * to shuffling around masses of pixel data. 355 */ 356 bandOffsets = new int[pixelStride]; 357 for (int i = 0; i < pixelStride; i++) 358 bandOffsets[i] = i; 359 360 /* FIXME: Think about whether it would make sense to return the 361 * possibly more efficient PixelInterleavedSampleModel for other 362 * transferTypes as well. It seems unlikely that this would break 363 * any user applications, so the Mauve tests on this method 364 * might be too restrictive. 365 */ 366 switch (transferType) 367 { 368 case DataBuffer.TYPE_BYTE: 369 case DataBuffer.TYPE_USHORT: 370 return new PixelInterleavedSampleModel(transferType, w, h, 371 pixelStride, 372 scanlineStride, 373 bandOffsets); 374 375 default: 376 return new ComponentSampleModel(transferType, w, h, 377 pixelStride, 378 scanlineStride, 379 bandOffsets); 380 } 381 } 382 383 384 public boolean isCompatibleSampleModel(SampleModel sm) 385 { 386 return 387 (sm instanceof ComponentSampleModel) && 388 super.isCompatibleSampleModel(sm); 389 } 390 391 public WritableRaster getAlphaRaster(WritableRaster raster) 392 { 393 if (!hasAlpha()) return null; 394 395 SampleModel sm = raster.getSampleModel(); 396 int[] alphaBand = { sm.getNumBands() - 1 }; 397 SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); 398 DataBuffer buffer = raster.getDataBuffer(); 399 Point origin = new Point(0, 0); 400 return Raster.createWritableRaster(alphaModel, buffer, origin); 401 } 402 403 public boolean equals(Object obj) 404 { 405 if (!(obj instanceof ComponentColorModel)) return false; 406 return super.equals(obj); 407 } 408 }