J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package java.awt.image; |
| 27 | |
| 28 | import java.awt.Transparency; |
| 29 | import java.awt.color.ColorSpace; |
| 30 | |
| 31 | /** |
| 32 | * The <code>PackedColorModel</code> class is an abstract |
| 33 | * {@link ColorModel} class that works with pixel values which represent |
| 34 | * color and alpha information as separate samples and which pack all |
| 35 | * samples for a single pixel into a single int, short, or byte quantity. |
| 36 | * This class can be used with an arbitrary {@link ColorSpace}. The number of |
| 37 | * color samples in the pixel values must be the same as the number of color |
| 38 | * components in the <code>ColorSpace</code>. There can be a single alpha |
| 39 | * sample. The array length is always 1 for those methods that use a |
| 40 | * primitive array pixel representation of type <code>transferType</code>. |
| 41 | * The transfer types supported are DataBuffer.TYPE_BYTE, |
| 42 | * DataBuffer.TYPE_USHORT, and DataBuffer.TYPE_INT. |
| 43 | * Color and alpha samples are stored in the single element of the array |
| 44 | * in bits indicated by bit masks. Each bit mask must be contiguous and |
| 45 | * masks must not overlap. The same masks apply to the single int |
| 46 | * pixel representation used by other methods. The correspondence of |
| 47 | * masks and color/alpha samples is as follows: |
| 48 | * <ul> |
| 49 | * <li> Masks are identified by indices running from 0 through |
| 50 | * {@link ColorModel#getNumComponents() getNumComponents} - 1. |
| 51 | * <li> The first |
| 52 | * {@link ColorModel#getNumColorComponents() getNumColorComponents} |
| 53 | * indices refer to color samples. |
| 54 | * <li> If an alpha sample is present, it corresponds the last index. |
| 55 | * <li> The order of the color indices is specified |
| 56 | * by the <code>ColorSpace</code>. Typically, this reflects the name of |
| 57 | * the color space type (for example, TYPE_RGB), index 0 |
| 58 | * corresponds to red, index 1 to green, and index 2 to blue. |
| 59 | * </ul> |
| 60 | * <p> |
| 61 | * The translation from pixel values to color/alpha components for |
| 62 | * display or processing purposes is a one-to-one correspondence of |
| 63 | * samples to components. |
| 64 | * A <code>PackedColorModel</code> is typically used with image data |
| 65 | * that uses masks to define packed samples. For example, a |
| 66 | * <code>PackedColorModel</code> can be used in conjunction with a |
| 67 | * {@link SinglePixelPackedSampleModel} to construct a |
| 68 | * {@link BufferedImage}. Normally the masks used by the |
| 69 | * {@link SampleModel} and the <code>ColorModel</code> would be the same. |
| 70 | * However, if they are different, the color interpretation of pixel data is |
| 71 | * done according to the masks of the <code>ColorModel</code>. |
| 72 | * <p> |
| 73 | * A single <code>int</code> pixel representation is valid for all objects |
| 74 | * of this class since it is always possible to represent pixel values |
| 75 | * used with this class in a single <code>int</code>. Therefore, methods |
| 76 | * that use this representation do not throw an |
| 77 | * <code>IllegalArgumentException</code> due to an invalid pixel value. |
| 78 | * <p> |
| 79 | * A subclass of <code>PackedColorModel</code> is {@link DirectColorModel}, |
| 80 | * which is similar to an X11 TrueColor visual. |
| 81 | * |
| 82 | * @see DirectColorModel |
| 83 | * @see SinglePixelPackedSampleModel |
| 84 | * @see BufferedImage |
| 85 | */ |
| 86 | |
| 87 | public abstract class PackedColorModel extends ColorModel { |
| 88 | int[] maskArray; |
| 89 | int[] maskOffsets; |
| 90 | float[] scaleFactors; |
| 91 | |
| 92 | /** |
| 93 | * Constructs a <code>PackedColorModel</code> from a color mask array, |
| 94 | * which specifies which bits in an <code>int</code> pixel representation |
| 95 | * contain each of the color samples, and an alpha mask. Color |
| 96 | * components are in the specified <code>ColorSpace</code>. The length of |
| 97 | * <code>colorMaskArray</code> should be the number of components in |
| 98 | * the <code>ColorSpace</code>. All of the bits in each mask |
| 99 | * must be contiguous and fit in the specified number of least significant |
| 100 | * bits of an <code>int</code> pixel representation. If the |
| 101 | * <code>alphaMask</code> is 0, there is no alpha. If there is alpha, |
| 102 | * the <code>boolean</code> <code>isAlphaPremultiplied</code> specifies |
| 103 | * how to interpret color and alpha samples in pixel values. If the |
| 104 | * <code>boolean</code> is <code>true</code>, color samples are assumed |
| 105 | * to have been multiplied by the alpha sample. The transparency, |
| 106 | * <code>trans</code>, specifies what alpha values can be represented |
| 107 | * by this color model. The transfer type is the type of primitive |
| 108 | * array used to represent pixel values. |
| 109 | * @param space the specified <code>ColorSpace</code> |
| 110 | * @param bits the number of bits in the pixel values |
| 111 | * @param colorMaskArray array that specifies the masks representing |
| 112 | * the bits of the pixel values that represent the color |
| 113 | * components |
| 114 | * @param alphaMask specifies the mask representing |
| 115 | * the bits of the pixel values that represent the alpha |
| 116 | * component |
| 117 | * @param isAlphaPremultiplied <code>true</code> if color samples are |
| 118 | * premultiplied by the alpha sample; <code>false</code> otherwise |
| 119 | * @param trans specifies the alpha value that can be represented by |
| 120 | * this color model |
| 121 | * @param transferType the type of array used to represent pixel values |
| 122 | * @throws IllegalArgumentException if <code>bits</code> is less than |
| 123 | * 1 or greater than 32 |
| 124 | */ |
| 125 | public PackedColorModel (ColorSpace space, int bits, |
| 126 | int[] colorMaskArray, int alphaMask, |
| 127 | boolean isAlphaPremultiplied, |
| 128 | int trans, int transferType) { |
| 129 | super(bits, PackedColorModel.createBitsArray(colorMaskArray, |
| 130 | alphaMask), |
| 131 | space, (alphaMask == 0 ? false : true), |
| 132 | isAlphaPremultiplied, trans, transferType); |
| 133 | if (bits < 1 || bits > 32) { |
| 134 | throw new IllegalArgumentException("Number of bits must be between" |
| 135 | +" 1 and 32."); |
| 136 | } |
| 137 | maskArray = new int[numComponents]; |
| 138 | maskOffsets = new int[numComponents]; |
| 139 | scaleFactors = new float[numComponents]; |
| 140 | |
| 141 | for (int i=0; i < numColorComponents; i++) { |
| 142 | // Get the mask offset and #bits |
| 143 | DecomposeMask(colorMaskArray[i], i, space.getName(i)); |
| 144 | } |
| 145 | if (alphaMask != 0) { |
| 146 | DecomposeMask(alphaMask, numColorComponents, "alpha"); |
| 147 | if (nBits[numComponents-1] == 1) { |
| 148 | transparency = Transparency.BITMASK; |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Constructs a <code>PackedColorModel</code> from the specified |
| 155 | * masks which indicate which bits in an <code>int</code> pixel |
| 156 | * representation contain the alpha, red, green and blue color samples. |
| 157 | * Color components are in the specified <code>ColorSpace</code>, which |
| 158 | * must be of type ColorSpace.TYPE_RGB. All of the bits in each |
| 159 | * mask must be contiguous and fit in the specified number of |
| 160 | * least significant bits of an <code>int</code> pixel representation. If |
| 161 | * <code>amask</code> is 0, there is no alpha. If there is alpha, |
| 162 | * the <code>boolean</code> <code>isAlphaPremultiplied</code> |
| 163 | * specifies how to interpret color and alpha samples |
| 164 | * in pixel values. If the <code>boolean</code> is <code>true</code>, |
| 165 | * color samples are assumed to have been multiplied by the alpha sample. |
| 166 | * The transparency, <code>trans</code>, specifies what alpha values |
| 167 | * can be represented by this color model. |
| 168 | * The transfer type is the type of primitive array used to represent |
| 169 | * pixel values. |
| 170 | * @param space the specified <code>ColorSpace</code> |
| 171 | * @param bits the number of bits in the pixel values |
| 172 | * @param rmask specifies the mask representing |
| 173 | * the bits of the pixel values that represent the red |
| 174 | * color component |
| 175 | * @param gmask specifies the mask representing |
| 176 | * the bits of the pixel values that represent the green |
| 177 | * color component |
| 178 | * @param bmask specifies the mask representing |
| 179 | * the bits of the pixel values that represent |
| 180 | * the blue color component |
| 181 | * @param amask specifies the mask representing |
| 182 | * the bits of the pixel values that represent |
| 183 | * the alpha component |
| 184 | * @param isAlphaPremultiplied <code>true</code> if color samples are |
| 185 | * premultiplied by the alpha sample; <code>false</code> otherwise |
| 186 | * @param trans specifies the alpha value that can be represented by |
| 187 | * this color model |
| 188 | * @param transferType the type of array used to represent pixel values |
| 189 | * @throws IllegalArgumentException if <code>space</code> is not a |
| 190 | * TYPE_RGB space |
| 191 | * @see ColorSpace |
| 192 | */ |
| 193 | public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask, |
| 194 | int bmask, int amask, |
| 195 | boolean isAlphaPremultiplied, |
| 196 | int trans, int transferType) { |
| 197 | super (bits, PackedColorModel.createBitsArray(rmask, gmask, bmask, |
| 198 | amask), |
| 199 | space, (amask == 0 ? false : true), |
| 200 | isAlphaPremultiplied, trans, transferType); |
| 201 | |
| 202 | if (space.getType() != ColorSpace.TYPE_RGB) { |
| 203 | throw new IllegalArgumentException("ColorSpace must be TYPE_RGB."); |
| 204 | } |
| 205 | maskArray = new int[numComponents]; |
| 206 | maskOffsets = new int[numComponents]; |
| 207 | scaleFactors = new float[numComponents]; |
| 208 | |
| 209 | DecomposeMask(rmask, 0, "red"); |
| 210 | |
| 211 | DecomposeMask(gmask, 1, "green"); |
| 212 | |
| 213 | DecomposeMask(bmask, 2, "blue"); |
| 214 | |
| 215 | if (amask != 0) { |
| 216 | DecomposeMask(amask, 3, "alpha"); |
| 217 | if (nBits[3] == 1) { |
| 218 | transparency = Transparency.BITMASK; |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Returns the mask indicating which bits in a pixel |
| 225 | * contain the specified color/alpha sample. For color |
| 226 | * samples, <code>index</code> corresponds to the placement of color |
| 227 | * sample names in the color space. Thus, an <code>index</code> |
| 228 | * equal to 0 for a CMYK ColorSpace would correspond to |
| 229 | * Cyan and an <code>index</code> equal to 1 would correspond to |
| 230 | * Magenta. If there is alpha, the alpha <code>index</code> would be: |
| 231 | * <pre> |
| 232 | * alphaIndex = numComponents() - 1; |
| 233 | * </pre> |
| 234 | * @param index the specified color or alpha sample |
| 235 | * @return the mask, which indicates which bits of the <code>int</code> |
| 236 | * pixel representation contain the color or alpha sample specified |
| 237 | * by <code>index</code>. |
| 238 | * @throws ArrayIndexOutOfBoundsException if <code>index</code> is |
| 239 | * greater than the number of components minus 1 in this |
| 240 | * <code>PackedColorModel</code> or if <code>index</code> is |
| 241 | * less than zero |
| 242 | */ |
| 243 | final public int getMask(int index) { |
| 244 | return maskArray[index]; |
| 245 | } |
| 246 | |
| 247 | /** |
| 248 | * Returns a mask array indicating which bits in a pixel |
| 249 | * contain the color and alpha samples. |
| 250 | * @return the mask array , which indicates which bits of the |
| 251 | * <code>int</code> pixel |
| 252 | * representation contain the color or alpha samples. |
| 253 | */ |
| 254 | final public int[] getMasks() { |
| 255 | return (int[]) maskArray.clone(); |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 | * A utility function to compute the mask offset and scalefactor, |
| 260 | * store these and the mask in instance arrays, and verify that |
| 261 | * the mask fits in the specified pixel size. |
| 262 | */ |
| 263 | private void DecomposeMask(int mask, int idx, String componentName) { |
| 264 | int off = 0; |
| 265 | int count = nBits[idx]; |
| 266 | |
| 267 | // Store the mask |
| 268 | maskArray[idx] = mask; |
| 269 | |
| 270 | // Now find the shift |
| 271 | if (mask != 0) { |
| 272 | while ((mask & 1) == 0) { |
| 273 | mask >>>= 1; |
| 274 | off++; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | if (off + count > pixel_bits) { |
| 279 | throw new IllegalArgumentException(componentName + " mask "+ |
| 280 | Integer.toHexString(maskArray[idx])+ |
| 281 | " overflows pixel (expecting "+ |
| 282 | pixel_bits+" bits"); |
| 283 | } |
| 284 | |
| 285 | maskOffsets[idx] = off; |
| 286 | if (count == 0) { |
| 287 | // High enough to scale any 0-ff value down to 0.0, but not |
| 288 | // high enough to get Infinity when scaling back to pixel bits |
| 289 | scaleFactors[idx] = 256.0f; |
| 290 | } else { |
| 291 | scaleFactors[idx] = 255.0f / ((1 << count) - 1); |
| 292 | } |
| 293 | |
| 294 | } |
| 295 | |
| 296 | /** |
| 297 | * Creates a <code>SampleModel</code> with the specified width and |
| 298 | * height that has a data layout compatible with this |
| 299 | * <code>ColorModel</code>. |
| 300 | * @param w the width (in pixels) of the region of the image data |
| 301 | * described |
| 302 | * @param h the height (in pixels) of the region of the image data |
| 303 | * described |
| 304 | * @return the newly created <code>SampleModel</code>. |
| 305 | * @throws IllegalArgumentException if <code>w</code> or |
| 306 | * <code>h</code> is not greater than 0 |
| 307 | * @see SampleModel |
| 308 | */ |
| 309 | public SampleModel createCompatibleSampleModel(int w, int h) { |
| 310 | return new SinglePixelPackedSampleModel(transferType, w, h, |
| 311 | maskArray); |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * Checks if the specified <code>SampleModel</code> is compatible |
| 316 | * with this <code>ColorModel</code>. If <code>sm</code> is |
| 317 | * <code>null</code>, this method returns <code>false</code>. |
| 318 | * @param sm the specified <code>SampleModel</code>, |
| 319 | * or <code>null</code> |
| 320 | * @return <code>true</code> if the specified <code>SampleModel</code> |
| 321 | * is compatible with this <code>ColorModel</code>; |
| 322 | * <code>false</code> otherwise. |
| 323 | * @see SampleModel |
| 324 | */ |
| 325 | public boolean isCompatibleSampleModel(SampleModel sm) { |
| 326 | if (! (sm instanceof SinglePixelPackedSampleModel)) { |
| 327 | return false; |
| 328 | } |
| 329 | |
| 330 | // Must have the same number of components |
| 331 | if (numComponents != sm.getNumBands()) { |
| 332 | return false; |
| 333 | } |
| 334 | |
| 335 | // Transfer type must be the same |
| 336 | if (sm.getTransferType() != transferType) { |
| 337 | return false; |
| 338 | } |
| 339 | |
| 340 | SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm; |
| 341 | // Now compare the specific masks |
| 342 | int[] bitMasks = sppsm.getBitMasks(); |
| 343 | if (bitMasks.length != maskArray.length) { |
| 344 | return false; |
| 345 | } |
| 346 | for (int i=0; i < bitMasks.length; i++) { |
| 347 | if (bitMasks[i] != maskArray[i]) { |
| 348 | return false; |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | return true; |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Returns a {@link WritableRaster} representing the alpha channel of |
| 357 | * an image, extracted from the input <code>WritableRaster</code>. |
| 358 | * This method assumes that <code>WritableRaster</code> objects |
| 359 | * associated with this <code>ColorModel</code> store the alpha band, |
| 360 | * if present, as the last band of image data. Returns <code>null</code> |
| 361 | * if there is no separate spatial alpha channel associated with this |
| 362 | * <code>ColorModel</code>. This method creates a new |
| 363 | * <code>WritableRaster</code>, but shares the data array. |
| 364 | * @param raster a <code>WritableRaster</code> containing an image |
| 365 | * @return a <code>WritableRaster</code> that represents the alpha |
| 366 | * channel of the image contained in <code>raster</code>. |
| 367 | */ |
| 368 | public WritableRaster getAlphaRaster(WritableRaster raster) { |
| 369 | if (hasAlpha() == false) { |
| 370 | return null; |
| 371 | } |
| 372 | |
| 373 | int x = raster.getMinX(); |
| 374 | int y = raster.getMinY(); |
| 375 | int[] band = new int[1]; |
| 376 | band[0] = raster.getNumBands() - 1; |
| 377 | return raster.createWritableChild(x, y, raster.getWidth(), |
| 378 | raster.getHeight(), x, y, |
| 379 | band); |
| 380 | } |
| 381 | |
| 382 | /** |
| 383 | * Tests if the specified <code>Object</code> is an instance |
| 384 | * of <code>PackedColorModel</code> and equals this |
| 385 | * <code>PackedColorModel</code>. |
| 386 | * @param obj the <code>Object</code> to test for equality |
| 387 | * @return <code>true</code> if the specified <code>Object</code> |
| 388 | * is an instance of <code>PackedColorModel</code> and equals this |
| 389 | * <code>PackedColorModel</code>; <code>false</code> otherwise. |
| 390 | */ |
| 391 | public boolean equals(Object obj) { |
| 392 | if (!(obj instanceof PackedColorModel)) { |
| 393 | return false; |
| 394 | } |
| 395 | |
| 396 | if (!super.equals(obj)) { |
| 397 | return false; |
| 398 | } |
| 399 | |
| 400 | PackedColorModel cm = (PackedColorModel) obj; |
| 401 | int numC = cm.getNumComponents(); |
| 402 | if (numC != numComponents) { |
| 403 | return false; |
| 404 | } |
| 405 | for(int i=0; i < numC; i++) { |
| 406 | if (maskArray[i] != cm.getMask(i)) { |
| 407 | return false; |
| 408 | } |
| 409 | } |
| 410 | return true; |
| 411 | } |
| 412 | |
| 413 | private final static int[] createBitsArray(int[]colorMaskArray, |
| 414 | int alphaMask) { |
| 415 | int numColors = colorMaskArray.length; |
| 416 | int numAlpha = (alphaMask == 0 ? 0 : 1); |
| 417 | int[] arr = new int[numColors+numAlpha]; |
| 418 | for (int i=0; i < numColors; i++) { |
| 419 | arr[i] = countBits(colorMaskArray[i]); |
| 420 | if (arr[i] < 0) { |
| 421 | throw new IllegalArgumentException("Noncontiguous color mask (" |
| 422 | + Integer.toHexString(colorMaskArray[i])+ |
| 423 | "at index "+i); |
| 424 | } |
| 425 | } |
| 426 | if (alphaMask != 0) { |
| 427 | arr[numColors] = countBits(alphaMask); |
| 428 | if (arr[numColors] < 0) { |
| 429 | throw new IllegalArgumentException("Noncontiguous alpha mask (" |
| 430 | + Integer.toHexString(alphaMask)); |
| 431 | } |
| 432 | } |
| 433 | return arr; |
| 434 | } |
| 435 | |
| 436 | private final static int[] createBitsArray(int rmask, int gmask, int bmask, |
| 437 | int amask) { |
| 438 | int[] arr = new int[3 + (amask == 0 ? 0 : 1)]; |
| 439 | arr[0] = countBits(rmask); |
| 440 | arr[1] = countBits(gmask); |
| 441 | arr[2] = countBits(bmask); |
| 442 | if (arr[0] < 0) { |
| 443 | throw new IllegalArgumentException("Noncontiguous red mask (" |
| 444 | + Integer.toHexString(rmask)); |
| 445 | } |
| 446 | else if (arr[1] < 0) { |
| 447 | throw new IllegalArgumentException("Noncontiguous green mask (" |
| 448 | + Integer.toHexString(gmask)); |
| 449 | } |
| 450 | else if (arr[2] < 0) { |
| 451 | throw new IllegalArgumentException("Noncontiguous blue mask (" |
| 452 | + Integer.toHexString(bmask)); |
| 453 | } |
| 454 | if (amask != 0) { |
| 455 | arr[3] = countBits(amask); |
| 456 | if (arr[3] < 0) { |
| 457 | throw new IllegalArgumentException("Noncontiguous alpha mask (" |
| 458 | + Integer.toHexString(amask)); |
| 459 | } |
| 460 | } |
| 461 | return arr; |
| 462 | } |
| 463 | |
| 464 | private final static int countBits(int mask) { |
| 465 | int count = 0; |
| 466 | if (mask != 0) { |
| 467 | while ((mask & 1) == 0) { |
| 468 | mask >>>= 1; |
| 469 | } |
| 470 | while ((mask & 1) == 1) { |
| 471 | mask >>>= 1; |
| 472 | count++; |
| 473 | } |
| 474 | } |
| 475 | if (mask != 0) { |
| 476 | return -1; |
| 477 | } |
| 478 | return count; |
| 479 | } |
| 480 | |
| 481 | } |