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 93 /** 94 * Constructs a {@code PackedColorModel} from a color mask array, 95 * which specifies which bits in an {@code int} pixel representation 96 * contain each of the color samples, and an alpha mask. Color 97 * components are in the specified {@code ColorSpace}. The length of 98 * {@code colorMaskArray} should be the number of components in 99 * the {@code ColorSpace}. All of the bits in each mask 100 * must be contiguous and fit in the specified number of least significant 101 * bits of an {@code int} pixel representation. If the 102 * {@code alphaMask} is 0, there is no alpha. If there is alpha, 103 * the {@code boolean isAlphaPremultiplied} specifies 104 * how to interpret color and alpha samples in pixel values. If the 105 * {@code boolean} is {@code true}, color samples are assumed 106 * to have been multiplied by the alpha sample. The transparency, 107 * {@code trans}, specifies what alpha values can be represented 108 * by this color model. The transfer type is the type of primitive 109 * array used to represent pixel values. 110 * @param space the specified {@code ColorSpace} 111 * @param bits the number of bits in the pixel values 112 * @param colorMaskArray array that specifies the masks representing 113 * the bits of the pixel values that represent the color 114 * components 115 * @param alphaMask specifies the mask representing 116 * the bits of the pixel values that represent the alpha 117 * component 118 * @param isAlphaPremultiplied {@code true} if color samples are 119 * premultiplied by the alpha sample; {@code false} otherwise 120 * @param trans specifies the alpha value that can be represented by 121 * this color model 122 * @param transferType the type of array used to represent pixel values 123 * @throws IllegalArgumentException if {@code bits} is less than 124 * 1 or greater than 32 125 */ 126 public PackedColorModel (ColorSpace space, int bits, 127 int[] colorMaskArray, int alphaMask, 128 boolean isAlphaPremultiplied, 129 int trans, int transferType) { 130 super(bits, PackedColorModel.createBitsArray(colorMaskArray, 131 alphaMask), 132 space, (alphaMask == 0 ? false : true), 133 isAlphaPremultiplied, trans, transferType); 134 if (bits < 1 || bits > 32) { 135 throw new IllegalArgumentException("Number of bits must be between" 136 +" 1 and 32."); 137 } 138 maskArray = new int[numComponents]; 139 maskOffsets = new int[numComponents]; 140 scaleFactors = new float[numComponents]; 141 142 for (int i=0; i < numColorComponents; i++) { 143 // Get the mask offset and #bits 144 DecomposeMask(colorMaskArray[i], i, space.getName(i)); 145 } 146 if (alphaMask != 0) { 147 DecomposeMask(alphaMask, numColorComponents, "alpha"); 148 if (nBits[numComponents-1] == 1) { 149 transparency = Transparency.BITMASK; 150 } 151 } 152 } 153 154 /** 155 * Constructs a {@code PackedColorModel} from the specified 156 * masks which indicate which bits in an {@code int} pixel 157 * representation contain the alpha, red, green and blue color samples. 158 * Color components are in the specified {@code ColorSpace}, which 159 * must be of type ColorSpace.TYPE_RGB. All of the bits in each 160 * mask must be contiguous and fit in the specified number of 161 * least significant bits of an {@code int} pixel representation. If 162 * {@code amask} is 0, there is no alpha. If there is alpha, 163 * the {@code boolean isAlphaPremultiplied} 164 * specifies how to interpret color and alpha samples 165 * in pixel values. If the {@code boolean} is {@code true}, 166 * color samples are assumed to have been multiplied by the alpha sample. 167 * The transparency, {@code trans}, specifies what alpha values 168 * can be represented by this color model. 169 * The transfer type is the type of primitive array used to represent 170 * pixel values. 171 * @param space the specified {@code ColorSpace} 172 * @param bits the number of bits in the pixel values 173 * @param rmask specifies the mask representing 174 * the bits of the pixel values that represent the red 175 * color component 176 * @param gmask specifies the mask representing 177 * the bits of the pixel values that represent the green 178 * color component 179 * @param bmask specifies the mask representing 180 * the bits of the pixel values that represent 181 * the blue color component 182 * @param amask specifies the mask representing 183 * the bits of the pixel values that represent 184 * the alpha component 185 * @param isAlphaPremultiplied {@code true} if color samples are 186 * premultiplied by the alpha sample; {@code false} otherwise 187 * @param trans specifies the alpha value that can be represented by 188 * this color model 189 * @param transferType the type of array used to represent pixel values 190 * @throws IllegalArgumentException if {@code space} is not a 191 * TYPE_RGB space 192 * @see ColorSpace 193 */ 194 public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask, 195 int bmask, int amask, 196 boolean isAlphaPremultiplied, 197 int trans, int transferType) { 198 super (bits, PackedColorModel.createBitsArray(rmask, gmask, bmask, 199 amask), 200 space, (amask == 0 ? false : true), 201 isAlphaPremultiplied, trans, transferType); 202 203 if (space.getType() != ColorSpace.TYPE_RGB) { 204 throw new IllegalArgumentException("ColorSpace must be TYPE_RGB."); 205 } 206 maskArray = new int[numComponents]; 207 maskOffsets = new int[numComponents]; 208 scaleFactors = new float[numComponents]; 209 210 DecomposeMask(rmask, 0, "red"); 211 212 DecomposeMask(gmask, 1, "green"); 213 214 DecomposeMask(bmask, 2, "blue"); 215 216 if (amask != 0) { 217 DecomposeMask(amask, 3, "alpha"); 218 if (nBits[3] == 1) { 219 transparency = Transparency.BITMASK; 220 } 221 } 222 } 223 224 /** 225 * Returns the mask indicating which bits in a pixel 226 * contain the specified color/alpha sample. For color 227 * samples, {@code index} corresponds to the placement of color 228 * sample names in the color space. Thus, an {@code index} 229 * equal to 0 for a CMYK ColorSpace would correspond to 230 * Cyan and an {@code index} equal to 1 would correspond to 231 * Magenta. If there is alpha, the alpha {@code index} would be: 232 * <pre> 233 * alphaIndex = numComponents() - 1; 234 * </pre> 235 * @param index the specified color or alpha sample 236 * @return the mask, which indicates which bits of the {@code int} 237 * pixel representation contain the color or alpha sample specified 238 * by {@code index}. 239 * @throws ArrayIndexOutOfBoundsException if {@code index} is 240 * greater than the number of components minus 1 in this 241 * {@code PackedColorModel} or if {@code index} is 242 * less than zero 243 */ 244 public final int getMask(int index) { 245 return maskArray[index]; 246 } 247 248 /** 249 * Returns a mask array indicating which bits in a pixel 250 * contain the color and alpha samples. 251 * @return the mask array , which indicates which bits of the 252 * {@code int} pixel 253 * representation contain the color or alpha samples. 254 */ 255 public final int[] getMasks() { 256 return maskArray.clone(); 257 } 258 259 /* 260 * A utility function to compute the mask offset and scalefactor, 261 * store these and the mask in instance arrays, and verify that 262 * the mask fits in the specified pixel size. 263 */ 264 private void DecomposeMask(int mask, int idx, String componentName) { 265 int off = 0; 266 int count = nBits[idx]; 267 268 // Store the mask 269 maskArray[idx] = mask; 270 271 // Now find the shift 272 if (mask != 0) { 273 while ((mask & 1) == 0) { 274 mask >>>= 1; 275 off++; 276 } 277 } 278 279 if (off + count > pixel_bits) { 280 throw new IllegalArgumentException(componentName + " mask "+ 281 Integer.toHexString(maskArray[idx])+ 282 " overflows pixel (expecting "+ 283 pixel_bits+" bits"); 284 } 285 286 maskOffsets[idx] = off; 287 if (count == 0) { 288 // High enough to scale any 0-ff value down to 0.0, but not 289 // high enough to get Infinity when scaling back to pixel bits 290 scaleFactors[idx] = 256.0f; 291 } else { 292 scaleFactors[idx] = 255.0f / ((1 << count) - 1); 293 } 294 295 } 296 297 /** 298 * Creates a {@code SampleModel} with the specified width and 299 * height that has a data layout compatible with this 300 * {@code ColorModel}. 301 * @param w the width (in pixels) of the region of the image data 302 * described 303 * @param h the height (in pixels) of the region of the image data 304 * described 305 * @return the newly created {@code SampleModel}. 306 * @throws IllegalArgumentException if {@code w} or 307 * {@code h} is not greater than 0 308 * @see SampleModel 309 */ 310 public SampleModel createCompatibleSampleModel(int w, int h) { 311 return new SinglePixelPackedSampleModel(transferType, w, h, 312 maskArray); 313 } 314 315 /** 316 * Checks if the specified {@code SampleModel} is compatible 317 * with this {@code ColorModel}. If {@code sm} is 318 * {@code null}, this method returns {@code false}. 319 * @param sm the specified {@code SampleModel}, 320 * or {@code null} 321 * @return {@code true} if the specified {@code SampleModel} 322 * is compatible with this {@code ColorModel}; 323 * {@code false} otherwise. 324 * @see SampleModel 325 */ 326 public boolean isCompatibleSampleModel(SampleModel sm) { 327 if (! (sm instanceof SinglePixelPackedSampleModel)) { 328 return false; 329 } 330 331 // Must have the same number of components 332 if (numComponents != sm.getNumBands()) { 333 return false; 334 } 335 336 // Transfer type must be the same 337 if (sm.getTransferType() != transferType) { 338 return false; 339 } 340 341 SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm; 342 // Now compare the specific masks 343 int[] bitMasks = sppsm.getBitMasks(); 344 if (bitMasks.length != maskArray.length) { 345 return false; 346 } 347 348 /* compare 'effective' masks only, i.e. only part of the mask 349 * which fits the capacity of the transfer type. 350 */ 351 int maxMask = (int)((1L << DataBuffer.getDataTypeSize(transferType)) - 1); 352 for (int i=0; i < bitMasks.length; i++) { 353 if ((maxMask & bitMasks[i]) != (maxMask & maskArray[i])) { 354 return false; 355 } 356 } 357 358 return true; 359 } 360 361 /** 362 * Returns a {@link WritableRaster} representing the alpha channel of 363 * an image, extracted from the input {@code WritableRaster}. 364 * This method assumes that {@code WritableRaster} objects 365 * associated with this {@code ColorModel} store the alpha band, 366 * if present, as the last band of image data. Returns {@code null} 367 * if there is no separate spatial alpha channel associated with this 368 * {@code ColorModel}. This method creates a new 369 * {@code WritableRaster}, but shares the data array. 370 * @param raster a {@code WritableRaster} containing an image 371 * @return a {@code WritableRaster} that represents the alpha 372 * channel of the image contained in {@code raster}. 373 */ 374 public WritableRaster getAlphaRaster(WritableRaster raster) { 375 if (hasAlpha() == false) { 376 return null; 377 } 378 379 int x = raster.getMinX(); 380 int y = raster.getMinY(); 381 int[] band = new int[1]; 382 band[0] = raster.getNumBands() - 1; 383 return raster.createWritableChild(x, y, raster.getWidth(), 384 raster.getHeight(), x, y, 385 band); 386 } 387 388 /** 389 * Tests if the specified {@code Object} equals this 390 * {@code PackedColorModel}. 391 * In order to protect the symmetry property of 392 * {@code (a.equals(b) == b.equals(a))}, 393 * the target object must be the exact same class as this 394 * object to evaluate as {equals}. 395 * @param obj the {@code Object} to test for equality 396 * @return {@code true} if the specified {@code Object} 397 * equals this {@code PackedColorModel}; {@code false} otherwise. 398 */ 399 @Override 400 public boolean equals(Object obj) { 401 /* 402 * We verify the type of argument obj in super.equals() where we check 403 * for equality of class name. 404 */ 405 if (!super.equals(obj)) { 406 return false; 407 } 408 409 PackedColorModel cm = (PackedColorModel) obj; 410 int numC = cm.getNumComponents(); 411 for(int i=0; i < numC; i++) { 412 if (maskArray[i] != cm.getMask(i)) { 413 return false; 414 } 415 } 416 return true; 417 } 418 419 /** 420 * Returns the hash code for this PackedColorModel. 421 * 422 * @return a hash code for this PackedColorModel. 423 */ 424 @Override 425 public int hashCode() { 426 int hash = super.hashCode(); 427 hash = 89 * hash + Arrays.hashCode(this.maskArray); 428 return hash; 429 } 430 431 private static final int[] createBitsArray(int[]colorMaskArray, 432 int alphaMask) { 433 int numColors = colorMaskArray.length; 434 int numAlpha = (alphaMask == 0 ? 0 : 1); 435 int[] arr = new int[numColors+numAlpha]; 436 for (int i=0; i < numColors; i++) { 437 arr[i] = countBits(colorMaskArray[i]); 438 if (arr[i] < 0) { 439 throw new IllegalArgumentException("Noncontiguous color mask (" 440 + Integer.toHexString(colorMaskArray[i])+ 441 "at index "+i); 442 } 443 } 444 if (alphaMask != 0) { 445 arr[numColors] = countBits(alphaMask); 446 if (arr[numColors] < 0) { 447 throw new IllegalArgumentException("Noncontiguous alpha mask (" 448 + Integer.toHexString(alphaMask)); 449 } 450 } 451 return arr; 452 } 453 454 private static final int[] createBitsArray(int rmask, int gmask, int bmask, 455 int amask) { 456 int[] arr = new int[3 + (amask == 0 ? 0 : 1)]; 457 arr[0] = countBits(rmask); 458 arr[1] = countBits(gmask); 459 arr[2] = countBits(bmask); 460 if (arr[0] < 0) { 461 throw new IllegalArgumentException("Noncontiguous red mask (" 462 + Integer.toHexString(rmask)); 463 } 464 else if (arr[1] < 0) { 465 throw new IllegalArgumentException("Noncontiguous green mask (" 466 + Integer.toHexString(gmask)); 467 } 468 else if (arr[2] < 0) { 469 throw new IllegalArgumentException("Noncontiguous blue mask (" 470 + Integer.toHexString(bmask)); 471 } 472 if (amask != 0) { 473 arr[3] = countBits(amask); 474 if (arr[3] < 0) { 475 throw new IllegalArgumentException("Noncontiguous alpha mask (" 476 + Integer.toHexString(amask)); 477 } 478 } 479 return arr; 480 } 481 482 private static final int countBits(int mask) { 483 int count = 0; 484 if (mask != 0) { 485 while ((mask & 1) == 0) { 486 mask >>>= 1; 487 } 488 while ((mask & 1) == 1) { 489 mask >>>= 1; 490 count++; 491 } 492 } 493 if (mask != 0) { 494 return -1; 495 } 496 return count; 497 } 498 499 }