001/* DirectColorModel.java -- 002 Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.awt.image; 040 041import gnu.java.awt.Buffers; 042 043import java.awt.Point; 044import java.awt.Transparency; 045import java.awt.color.ColorSpace; 046 047/** 048 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 049 * @author C. Brian Jones (cbj@gnu.org) 050 * @author Mark Benvenuto (mcb54@columbia.edu) 051 */ 052public class DirectColorModel extends PackedColorModel 053{ 054 /** 055 * For the color model created with this constructor the pixels 056 * will have fully opaque alpha components with a value of 255. 057 * Each mask should describe a fully contiguous set of bits in the 058 * most likely order of alpha, red, green, blue from the most significant 059 * byte to the least significant byte. 060 * 061 * @param pixelBits the number of bits wide used for bit size of pixel values 062 * @param rmask the bits describing the red component of a pixel 063 * @param gmask the bits describing the green component of a pixel 064 * @param bmask the bits describing the blue component of a pixel 065 */ 066 public DirectColorModel(int pixelBits, int rmask, int gmask, int bmask) 067 { 068 this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, 069 rmask, gmask, bmask, 0, 070 false, // not alpha premultiplied 071 Buffers.smallestAppropriateTransferType(pixelBits) // find type 072 ); 073 } 074 075 /** 076 * For the color model created with this constructor the pixels 077 * will have fully opaque alpha components with a value of 255. 078 * Each mask should describe a fully contiguous set of bits in the 079 * most likely order of red, green, blue from the most significant 080 * byte to the least significant byte. 081 * 082 * @param pixelBits the number of bits wide used for bit size of pixel values 083 * @param rmask the bits describing the red component of a pixel 084 * @param gmask the bits describing the green component of a pixel 085 * @param bmask the bits describing the blue component of a pixel 086 * @param amask the bits describing the alpha component of a pixel 087 */ 088 public DirectColorModel(int pixelBits, 089 int rmask, int gmask, int bmask, int amask) 090 { 091 this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, 092 rmask, gmask, bmask, amask, 093 false, // not alpha premultiplied 094 Buffers.smallestAppropriateTransferType(pixelBits) // find type 095 ); 096 } 097 098 public DirectColorModel(ColorSpace cspace, int pixelBits, 099 int rmask, int gmask, int bmask, int amask, 100 boolean isAlphaPremultiplied, 101 int transferType) 102 { 103 super(cspace, pixelBits, 104 rmask, gmask, bmask, amask, isAlphaPremultiplied, 105 ((amask == 0) ? Transparency.OPAQUE : Transparency.TRANSLUCENT), 106 transferType); 107 } 108 109 public final int getRedMask() 110 { 111 return getMask(0); 112 } 113 114 public final int getGreenMask() 115 { 116 return getMask(1); 117 } 118 119 public final int getBlueMask() 120 { 121 return getMask(2); 122 } 123 124 public final int getAlphaMask() 125 { 126 return hasAlpha() ? getMask(3) : 0; 127 } 128 129 /** 130 * Get the red component of the given pixel. 131 * <br> 132 */ 133 public final int getRed(int pixel) 134 { 135 return extractAndNormalizeSample(pixel, 0); 136 } 137 138 /** 139 * Get the green component of the given pixel. 140 * <br> 141 */ 142 public final int getGreen(int pixel) 143 { 144 return extractAndNormalizeSample(pixel, 1); 145 } 146 147 /** 148 * Get the blue component of the given pixel. 149 * <br> 150 */ 151 public final int getBlue(int pixel) 152 { 153 return extractAndNormalizeSample(pixel, 2); 154 } 155 156 /** 157 * Get the alpha component of the given pixel. 158 * <br> 159 */ 160 public final int getAlpha(int pixel) 161 { 162 if (!hasAlpha()) 163 return 255; 164 return extractAndScaleSample(pixel, 3); 165 } 166 167 private int extractAndNormalizeSample(int pixel, int component) 168 { 169 int value = extractAndScaleSample(pixel, component); 170 if (hasAlpha() && isAlphaPremultiplied() && getAlpha(pixel) != 0) 171 value = value*255/getAlpha(pixel); 172 return value; 173 } 174 175 private int extractAndScaleSample(int pixel, int component) 176 { 177 int field = pixel & getMask(component); 178 int to8BitShift = 179 8 - shifts[component] - getComponentSize(component); 180 return (to8BitShift>0) ? 181 (field << to8BitShift) : 182 (field >>> (-to8BitShift)); 183 } 184 185 /** 186 * Get the RGB color value of the given pixel using the default 187 * RGB color model. 188 * <br> 189 * 190 * @param pixel a pixel value 191 */ 192 public final int getRGB(int pixel) 193 { 194 /* FIXME: The Sun docs show that this method is overridden, but I 195 don't see any way to improve on the superclass 196 implementation. */ 197 return super.getRGB(pixel); 198 } 199 200 public int getRed(Object inData) 201 { 202 return getRed(getPixelFromArray(inData)); 203 } 204 205 public int getGreen(Object inData) 206 { 207 return getGreen(getPixelFromArray(inData)); 208 } 209 210 public int getBlue(Object inData) 211 { 212 return getBlue(getPixelFromArray(inData)); 213 } 214 215 public int getAlpha(Object inData) 216 { 217 return getAlpha(getPixelFromArray(inData)); 218 } 219 220 public int getRGB(Object inData) 221 { 222 return getRGB(getPixelFromArray(inData)); 223 } 224 225 /** 226 * Converts a normalized pixel int value in the sRGB color 227 * space to an array containing a single pixel of the color space 228 * of the color model. 229 * 230 * <p>This method performs the inverse function of 231 * <code>getRGB(Object inData)</code>. 232 * 233 * @param rgb pixel as a normalized sRGB, 0xAARRGGBB value. 234 * 235 * @param pixel to avoid needless creation of arrays, an array to 236 * use to return the pixel can be given. If null, a suitable array 237 * will be created. 238 * 239 * @return array of transferType containing a single pixel. The 240 * pixel should be encoded in the natural way of the color model. 241 * 242 * @see #getRGB(Object) 243 */ 244 public Object getDataElements(int rgb, Object pixel) 245 { 246 // FIXME: handle alpha multiply 247 248 int pixelValue = 0; 249 int a = 0; 250 if (hasAlpha()) { 251 a = (rgb >>> 24) & 0xff; 252 pixelValue = valueToField(a, 3, 8); 253 } 254 255 if (hasAlpha() && isAlphaPremultiplied()) 256 { 257 int r, g, b; 258 /* if r=0xff and a=0xff, then resulting 259 value will be (r*a)>>>8 == 0xfe... This seems wrong. 260 We should divide by 255 rather than shifting >>>8 after 261 multiplying. 262 263 Too bad, shifting is probably less expensive. 264 r = ((rgb >>> 16) & 0xff)*a; 265 g = ((rgb >>> 8) & 0xff)*a; 266 b = ((rgb >>> 0) & 0xff)*a; */ 267 /* The r, g, b values we calculate are 16 bit. This allows 268 us to avoid discarding the lower 8 bits obtained if 269 multiplying with the alpha band. */ 270 271 // using 16 bit values 272 r = ((rgb >>> 8) & 0xff00)*a/255; 273 g = ((rgb >>> 0) & 0xff00)*a/255; 274 b = ((rgb << 8) & 0xff00)*a/255; 275 pixelValue |= 276 valueToField(r, 0, 16) | // Red 277 valueToField(g, 1, 16) | // Green 278 valueToField(b, 2, 16); // Blue 279 } 280 else 281 { 282 int r, g, b; 283 // using 8 bit values 284 r = (rgb >>> 16) & 0xff; 285 g = (rgb >>> 8) & 0xff; 286 b = (rgb >>> 0) & 0xff; 287 288 pixelValue |= 289 valueToField(r, 0, 8) | // Red 290 valueToField(g, 1, 8) | // Green 291 valueToField(b, 2, 8); // Blue 292 } 293 294 /* In this color model, the whole pixel fits in the first element 295 of the array. */ 296 DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 1); 297 buffer.setElem(0, pixelValue); 298 return Buffers.getData(buffer); 299 } 300 301 /** 302 * Converts a value to the correct field bits based on the 303 * information derived from the field masks. 304 * 305 * @param highBit the position of the most significant bit in the 306 * val parameter. 307 */ 308 private int valueToField(int val, int component, int highBit) 309 { 310 int toFieldShift = 311 getComponentSize(component) + shifts[component] - highBit; 312 int ret = (toFieldShift>0) ? 313 (val << toFieldShift) : 314 (val >>> (-toFieldShift)); 315 return ret & getMask(component); 316 } 317 318 /** 319 * Converts a 16 bit value to the correct field bits based on the 320 * information derived from the field masks. 321 */ 322 private int value16ToField(int val, int component) 323 { 324 int toFieldShift = getComponentSize(component) + shifts[component] - 16; 325 return (toFieldShift>0) ? 326 (val << toFieldShift) : 327 (val >>> (-toFieldShift)); 328 } 329 330 /** 331 * Fills an array with the unnormalized component samples from a 332 * pixel value. I.e. decompose the pixel, but not perform any 333 * color conversion. 334 */ 335 public final int[] getComponents(int pixel, int[] components, int offset) 336 { 337 int numComponents = getNumComponents(); 338 if (components == null) components = new int[offset + numComponents]; 339 340 for (int b=0; b<numComponents; b++) 341 components[offset++] = (pixel&getMask(b)) >>> shifts[b]; 342 343 return components; 344 } 345 346 public final int[] getComponents(Object pixel, int[] components, 347 int offset) 348 { 349 return getComponents(getPixelFromArray(pixel), components, offset); 350 } 351 352 /** 353 * Creates a <code>WriteableRaster</code> that has a <code>SampleModel</code> 354 * that is compatible with this <code>ColorModel</code>. 355 * 356 * @param w the width of the writeable raster to create 357 * @param h the height of the writeable raster to create 358 * 359 * @throws IllegalArgumentException if <code>w</code> or <code>h</code> 360 * is less than or equal to zero 361 */ 362 public final WritableRaster createCompatibleWritableRaster(int w, int h) 363 { 364 // Sun also makes this check here. 365 if(w <= 0 || h <= 0) 366 throw new IllegalArgumentException("width (=" + w + ") and height (=" 367 + h + ") must be > 0"); 368 369 SampleModel sm = createCompatibleSampleModel(w, h); 370 Point origin = new Point(0, 0); 371 return Raster.createWritableRaster(sm, origin); 372 } 373 374 public int getDataElement(int[] components, int offset) 375 { 376 int numComponents = getNumComponents(); 377 int pixelValue = 0; 378 379 for (int c=0; c<numComponents; c++) 380 pixelValue |= (components[offset++] << shifts[c]) & getMask(c); 381 382 return pixelValue; 383 } 384 385 public Object getDataElements(int[] components, int offset, Object obj) 386 { 387 /* In this color model, the whole pixel fits in the first element 388 of the array. */ 389 int pixelValue = getDataElement(components, offset); 390 391 DataBuffer buffer = Buffers.createBuffer(transferType, obj, 1); 392 buffer.setElem(0, pixelValue); 393 return Buffers.getData(buffer); 394 } 395 396 public final ColorModel coerceData (WritableRaster raster, 397 boolean isAlphaPremultiplied) 398 { 399 if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha()) 400 return this; 401 402 /* TODO: provide better implementation based on the 403 assumptions we can make due to the specific type of the 404 color model. */ 405 coerceDataWorker(raster, isAlphaPremultiplied); 406 407 return new DirectColorModel(cspace, pixel_bits, getRedMask(), 408 getGreenMask(), getBlueMask(), getAlphaMask(), 409 isAlphaPremultiplied, transferType); 410 } 411 412 public boolean isCompatibleRaster(Raster raster) 413 { 414 /* FIXME: the Sun docs say this method is overridden here, 415 but I don't see any way to improve upon the implementation 416 in ColorModel. */ 417 return super.isCompatibleRaster(raster); 418 } 419 420 String stringParam() 421 { 422 return super.stringParam() + 423 ", redMask=" + Integer.toHexString(getRedMask()) + 424 ", greenMask=" + Integer.toHexString(getGreenMask()) + 425 ", blueMask=" + Integer.toHexString(getBlueMask()) + 426 ", alphaMask=" + Integer.toHexString(getAlphaMask()); 427 } 428 429 public String toString() 430 { 431 /* FIXME: Again, docs say override, but how do we improve upon the 432 superclass implementation? */ 433 return super.toString(); 434 } 435} 436