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