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