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</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}&nbsp;-&nbsp;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 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 
 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</code>.
 363      * This method assumes that <code>WritableRaster</code> objects
 364      * associated with this <code>ColorModel</code> store the alpha band,
 365      * if present, as the last band of image data.  Returns <code>null</code>
 366      * if there is no separate spatial alpha channel associated with this
 367      * <code>ColorModel</code>.  This method creates a new
 368      * <code>WritableRaster</code>, but shares the data array.
 369      * @param raster a <code>WritableRaster</code> containing an image
 370      * @return a <code>WritableRaster</code> that represents the alpha
 371      *         channel of the image contained in <code>raster</code>.
 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</code> is an instance
 389      * of <code>PackedColorModel</code> and equals this
 390      * <code>PackedColorModel</code>.
 391      * @param obj the <code>Object</code> to test for equality
 392      * @return <code>true</code> if the specified <code>Object</code>
 393      * is an instance of <code>PackedColorModel</code> and equals this
 394      * <code>PackedColorModel</code>; <code>false</code> 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         if (numC != numComponents) {
 408             return false;
 409         }
 410         for(int i=0; i < numC; i++) {
 411             if (maskArray[i] != cm.getMask(i)) {
 412                 return false;
 413             }
 414         }
 415         return true;
 416     }
 417 
 418     private final static int[] createBitsArray(int[]colorMaskArray,
 419                                                int alphaMask) {
 420         int numColors = colorMaskArray.length;
 421         int numAlpha = (alphaMask == 0 ? 0 : 1);
 422         int[] arr = new int[numColors+numAlpha];
 423         for (int i=0; i < numColors; i++) {
 424             arr[i] = countBits(colorMaskArray[i]);
 425             if (arr[i] < 0) {
 426                 throw new IllegalArgumentException("Noncontiguous color mask ("
 427                                      + Integer.toHexString(colorMaskArray[i])+
 428                                      "at index "+i);
 429             }
 430         }
 431         if (alphaMask != 0) {
 432             arr[numColors] = countBits(alphaMask);
 433             if (arr[numColors] < 0) {
 434                 throw new IllegalArgumentException("Noncontiguous alpha mask ("
 435                                      + Integer.toHexString(alphaMask));
 436             }
 437         }
 438         return arr;
 439     }
 440 
 441     private final static int[] createBitsArray(int rmask, int gmask, int bmask,
 442                                          int amask) {
 443         int[] arr = new int[3 + (amask == 0 ? 0 : 1)];
 444         arr[0] = countBits(rmask);
 445         arr[1] = countBits(gmask);
 446         arr[2] = countBits(bmask);
 447         if (arr[0] < 0) {
 448             throw new IllegalArgumentException("Noncontiguous red mask ("
 449                                      + Integer.toHexString(rmask));
 450         }
 451         else if (arr[1] < 0) {
 452             throw new IllegalArgumentException("Noncontiguous green mask ("
 453                                      + Integer.toHexString(gmask));
 454         }
 455         else if (arr[2] < 0) {
 456             throw new IllegalArgumentException("Noncontiguous blue mask ("
 457                                      + Integer.toHexString(bmask));
 458         }
 459         if (amask != 0) {
 460             arr[3] = countBits(amask);
 461             if (arr[3] < 0) {
 462                 throw new IllegalArgumentException("Noncontiguous alpha mask ("
 463                                      + Integer.toHexString(amask));
 464             }
 465         }
 466         return arr;
 467     }
 468 
 469     private final static int countBits(int mask) {
 470         int count = 0;
 471         if (mask != 0) {
 472             while ((mask & 1) == 0) {
 473                 mask >>>= 1;
 474             }
 475             while ((mask & 1) == 1) {
 476                 mask >>>= 1;
 477                 count++;
 478             }
 479         }
 480         if (mask != 0) {
 481             return -1;
 482         }
 483         return count;
 484     }
 485 
 486 }