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 31 /** 32 * The {@code PackedColorModel} 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}. 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}. 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}. 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} is typically used with image data 65 * that uses masks to define packed samples. For example, a 66 * {@code PackedColorModel} 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} 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}. 72 * <p> 73 * A single {@code int} 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}. Therefore, methods 76 * that use this representation do not throw an 77 * {@code IllegalArgumentException} due to an invalid pixel value. 78 * <p> 79 * A subclass of {@code PackedColorModel} 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} from a color mask array, 94 * which specifies which bits in an {@code int} pixel representation 95 * contain each of the color samples, and an alpha mask. Color 96 * components are in the specified {@code ColorSpace}. The length of 97 * {@code colorMaskArray} should be the number of components in 98 * the {@code ColorSpace}. 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} pixel representation. If the 101 * {@code alphaMask} is 0, there is no alpha. If there is alpha, 102 * the {@code boolean isAlphaPremultiplied} specifies 103 * how to interpret color and alpha samples in pixel values. If the 104 * {@code boolean} is {@code true}, color samples are assumed 105 * to have been multiplied by the alpha sample. The transparency, 106 * {@code trans}, 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} 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} if color samples are 118 * premultiplied by the alpha sample; {@code false} 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} 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} from the specified 155 * masks which indicate which bits in an {@code int} pixel 156 * representation contain the alpha, red, green and blue color samples. 157 * Color components are in the specified {@code ColorSpace}, 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} pixel representation. If 161 * {@code amask} is 0, there is no alpha. If there is alpha, 162 * the {@code boolean isAlphaPremultiplied} 163 * specifies how to interpret color and alpha samples 164 * in pixel values. If the {@code boolean} is {@code true}, 165 * color samples are assumed to have been multiplied by the alpha sample. 166 * The transparency, {@code trans}, 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} 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} if color samples are 185 * premultiplied by the alpha sample; {@code false} 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} 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} corresponds to the placement of color 227 * sample names in the color space. Thus, an {@code index} 228 * equal to 0 for a CMYK ColorSpace would correspond to 229 * Cyan and an {@code index} equal to 1 would correspond to 230 * Magenta. If there is alpha, the alpha {@code index} 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} 236 * pixel representation contain the color or alpha sample specified 237 * by {@code index}. 238 * @throws ArrayIndexOutOfBoundsException if {@code index} is 239 * greater than the number of components minus 1 in this 240 * {@code PackedColorModel} or if {@code index} is 241 * less than zero 242 */ 243 public final 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} pixel 252 * representation contain the color or alpha samples. 253 */ 254 public final int[] getMasks() { 255 return 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} with the specified width and 298 * height that has a data layout compatible with this 299 * {@code ColorModel}. 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}. 305 * @throws IllegalArgumentException if {@code w} or 306 * {@code h} 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} is compatible 316 * with this {@code ColorModel}. If {@code sm} is 317 * {@code null}, this method returns {@code false}. 318 * @param sm the specified {@code SampleModel}, 319 * or {@code null} 320 * @return {@code true} if the specified {@code SampleModel} 321 * is compatible with this {@code ColorModel}; 322 * {@code false} 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 347 /* compare 'effective' masks only, i.e. only part of the mask 348 * which fits the capacity of the transfer type. 349 */ 350 int maxMask = (int)((1L << DataBuffer.getDataTypeSize(transferType)) - 1); 351 for (int i=0; i < bitMasks.length; i++) { 352 if ((maxMask & bitMasks[i]) != (maxMask & maskArray[i])) { 353 return false; 354 } 355 } 356 357 return true; 358 } 359 360 /** 361 * Returns a {@link WritableRaster} representing the alpha channel of 362 * an image, extracted from the input {@code WritableRaster}. 363 * This method assumes that {@code WritableRaster} objects 364 * associated with this {@code ColorModel} store the alpha band, 365 * if present, as the last band of image data. Returns {@code null} 366 * if there is no separate spatial alpha channel associated with this 367 * {@code ColorModel}. This method creates a new 368 * {@code WritableRaster}, but shares the data array. 369 * @param raster a {@code WritableRaster} containing an image 370 * @return a {@code WritableRaster} that represents the alpha 371 * channel of the image contained in {@code raster}. 372 */ 373 public WritableRaster getAlphaRaster(WritableRaster raster) { 374 if (hasAlpha() == false) { 375 return null; 376 } 377 378 int x = raster.getMinX(); 379 int y = raster.getMinY(); 380 int[] band = new int[1]; 381 band[0] = raster.getNumBands() - 1; 382 return raster.createWritableChild(x, y, raster.getWidth(), 383 raster.getHeight(), x, y, 384 band); 385 } 386 387 /** 388 * Tests if the specified {@code Object} is an instance 389 * of {@code PackedColorModel} and equals this 390 * {@code PackedColorModel}. 391 * @param obj the {@code Object} to test for equality 392 * @return {@code true} if the specified {@code Object} 393 * is an instance of {@code PackedColorModel} and equals this 394 * {@code PackedColorModel}; {@code false} otherwise. 395 */ 396 public boolean equals(Object obj) { 397 if (!(obj instanceof PackedColorModel)) { 398 return false; 399 } 400 401 if (!super.equals(obj)) { 402 return false; 403 } 404 405 PackedColorModel cm = (PackedColorModel) obj; 406 int numC = cm.getNumComponents(); 407 for(int i=0; i < numC; i++) { 408 if (maskArray[i] != cm.getMask(i)) { 409 return false; 410 } 411 } 412 return true; 413 } 414 415 private static final int[] createBitsArray(int[]colorMaskArray, 416 int alphaMask) { 417 int numColors = colorMaskArray.length; 418 int numAlpha = (alphaMask == 0 ? 0 : 1); 419 int[] arr = new int[numColors+numAlpha]; 420 for (int i=0; i < numColors; i++) { 421 arr[i] = countBits(colorMaskArray[i]); 422 if (arr[i] < 0) { 423 throw new IllegalArgumentException("Noncontiguous color mask (" 424 + Integer.toHexString(colorMaskArray[i])+ 425 "at index "+i); 426 } 427 } 428 if (alphaMask != 0) { 429 arr[numColors] = countBits(alphaMask); 430 if (arr[numColors] < 0) { 431 throw new IllegalArgumentException("Noncontiguous alpha mask (" 432 + Integer.toHexString(alphaMask)); 433 } 434 } 435 return arr; 436 } 437 438 private static final int[] createBitsArray(int rmask, int gmask, int bmask, 439 int amask) { 440 int[] arr = new int[3 + (amask == 0 ? 0 : 1)]; 441 arr[0] = countBits(rmask); 442 arr[1] = countBits(gmask); 443 arr[2] = countBits(bmask); 444 if (arr[0] < 0) { 445 throw new IllegalArgumentException("Noncontiguous red mask (" 446 + Integer.toHexString(rmask)); 447 } 448 else if (arr[1] < 0) { 449 throw new IllegalArgumentException("Noncontiguous green mask (" 450 + Integer.toHexString(gmask)); 451 } 452 else if (arr[2] < 0) { 453 throw new IllegalArgumentException("Noncontiguous blue mask (" 454 + Integer.toHexString(bmask)); 455 } 456 if (amask != 0) { 457 arr[3] = countBits(amask); 458 if (arr[3] < 0) { 459 throw new IllegalArgumentException("Noncontiguous alpha mask (" 460 + Integer.toHexString(amask)); 461 } 462 } 463 return arr; 464 } 465 466 private static final int countBits(int mask) { 467 int count = 0; 468 if (mask != 0) { 469 while ((mask & 1) == 0) { 470 mask >>>= 1; 471 } 472 while ((mask & 1) == 1) { 473 mask >>>= 1; 474 count++; 475 } 476 } 477 if (mask != 0) { 478 return -1; 479 } 480 return count; 481 } 482 483 }