jdk/src/java.desktop/share/classes/java/awt/image/PackedColorModel.java
changeset 25859 3317bb8137f4
parent 22584 eed64ee05369
child 32865 f9cb6e427f9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/java/awt/image/PackedColorModel.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.awt.image;
+
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+
+/**
+ * The <code>PackedColorModel</code> class is an abstract
+ * {@link ColorModel} class that works with pixel values which represent
+ * color and alpha information as separate samples and which pack all
+ * samples for a single pixel into a single int, short, or byte quantity.
+ * This class can be used with an arbitrary {@link ColorSpace}.  The number of
+ * color samples in the pixel values must be the same as the number of color
+ * components in the <code>ColorSpace</code>.  There can be a single alpha
+ * sample.  The array length is always 1 for those methods that use a
+ * primitive array pixel representation of type <code>transferType</code>.
+ * The transfer types supported are DataBuffer.TYPE_BYTE,
+ * DataBuffer.TYPE_USHORT, and DataBuffer.TYPE_INT.
+ * Color and alpha samples are stored in the single element of the array
+ * in bits indicated by bit masks.  Each bit mask must be contiguous and
+ * masks must not overlap.  The same masks apply to the single int
+ * pixel representation used by other methods.  The correspondence of
+ * masks and color/alpha samples is as follows:
+ * <ul>
+ * <li> Masks are identified by indices running from 0 through
+ * {@link ColorModel#getNumComponents() getNumComponents}&nbsp;-&nbsp;1.
+ * <li> The first
+ * {@link ColorModel#getNumColorComponents() getNumColorComponents}
+ * indices refer to color samples.
+ * <li> If an alpha sample is present, it corresponds the last index.
+ * <li> The order of the color indices is specified
+ * by the <code>ColorSpace</code>.  Typically, this reflects the name of
+ * the color space type (for example, TYPE_RGB), index 0
+ * corresponds to red, index 1 to green, and index 2 to blue.
+ * </ul>
+ * <p>
+ * The translation from pixel values to color/alpha components for
+ * display or processing purposes is a one-to-one correspondence of
+ * samples to components.
+ * A <code>PackedColorModel</code> is typically used with image data
+ * that uses masks to define packed samples.  For example, a
+ * <code>PackedColorModel</code> can be used in conjunction with a
+ * {@link SinglePixelPackedSampleModel} to construct a
+ * {@link BufferedImage}.  Normally the masks used by the
+ * {@link SampleModel} and the <code>ColorModel</code> would be the same.
+ * However, if they are different, the color interpretation of pixel data is
+ * done according to the masks of the <code>ColorModel</code>.
+ * <p>
+ * A single <code>int</code> pixel representation is valid for all objects
+ * of this class since it is always possible to represent pixel values
+ * used with this class in a single <code>int</code>.  Therefore, methods
+ * that use this representation do not throw an
+ * <code>IllegalArgumentException</code> due to an invalid pixel value.
+ * <p>
+ * A subclass of <code>PackedColorModel</code> is {@link DirectColorModel},
+ * which is similar to an X11 TrueColor visual.
+ *
+ * @see DirectColorModel
+ * @see SinglePixelPackedSampleModel
+ * @see BufferedImage
+ */
+
+public abstract class PackedColorModel extends ColorModel {
+    int[] maskArray;
+    int[] maskOffsets;
+    float[] scaleFactors;
+
+    /**
+     * Constructs a <code>PackedColorModel</code> from a color mask array,
+     * which specifies which bits in an <code>int</code> pixel representation
+     * contain each of the color samples, and an alpha mask.  Color
+     * components are in the specified <code>ColorSpace</code>.  The length of
+     * <code>colorMaskArray</code> should be the number of components in
+     * the <code>ColorSpace</code>.  All of the bits in each mask
+     * must be contiguous and fit in the specified number of least significant
+     * bits of an <code>int</code> pixel representation.  If the
+     * <code>alphaMask</code> is 0, there is no alpha.  If there is alpha,
+     * the <code>boolean</code> <code>isAlphaPremultiplied</code> specifies
+     * how to interpret color and alpha samples in pixel values.  If the
+     * <code>boolean</code> is <code>true</code>, color samples are assumed
+     * to have been multiplied by the alpha sample.  The transparency,
+     * <code>trans</code>, specifies what alpha values can be represented
+     * by this color model.  The transfer type is the type of primitive
+     * array used to represent pixel values.
+     * @param space the specified <code>ColorSpace</code>
+     * @param bits the number of bits in the pixel values
+     * @param colorMaskArray array that specifies the masks representing
+     *         the bits of the pixel values that represent the color
+     *         components
+     * @param alphaMask specifies the mask representing
+     *         the bits of the pixel values that represent the alpha
+     *         component
+     * @param isAlphaPremultiplied <code>true</code> if color samples are
+     *        premultiplied by the alpha sample; <code>false</code> otherwise
+     * @param trans specifies the alpha value that can be represented by
+     *        this color model
+     * @param transferType the type of array used to represent pixel values
+     * @throws IllegalArgumentException if <code>bits</code> is less than
+     *         1 or greater than 32
+     */
+    public PackedColorModel (ColorSpace space, int bits,
+                             int[] colorMaskArray, int alphaMask,
+                             boolean isAlphaPremultiplied,
+                             int trans, int transferType) {
+        super(bits, PackedColorModel.createBitsArray(colorMaskArray,
+                                                     alphaMask),
+              space, (alphaMask == 0 ? false : true),
+              isAlphaPremultiplied, trans, transferType);
+        if (bits < 1 || bits > 32) {
+            throw new IllegalArgumentException("Number of bits must be between"
+                                               +" 1 and 32.");
+        }
+        maskArray   = new int[numComponents];
+        maskOffsets = new int[numComponents];
+        scaleFactors = new float[numComponents];
+
+        for (int i=0; i < numColorComponents; i++) {
+            // Get the mask offset and #bits
+            DecomposeMask(colorMaskArray[i], i, space.getName(i));
+        }
+        if (alphaMask != 0) {
+            DecomposeMask(alphaMask, numColorComponents, "alpha");
+            if (nBits[numComponents-1] == 1) {
+                transparency = Transparency.BITMASK;
+            }
+        }
+    }
+
+    /**
+     * Constructs a <code>PackedColorModel</code> from the specified
+     * masks which indicate which bits in an <code>int</code> pixel
+     * representation contain the alpha, red, green and blue color samples.
+     * Color components are in the specified <code>ColorSpace</code>, which
+     * must be of type ColorSpace.TYPE_RGB.  All of the bits in each
+     * mask must be contiguous and fit in the specified number of
+     * least significant bits of an <code>int</code> pixel representation.  If
+     * <code>amask</code> is 0, there is no alpha.  If there is alpha,
+     * the <code>boolean</code> <code>isAlphaPremultiplied</code>
+     * specifies how to interpret color and alpha samples
+     * in pixel values.  If the <code>boolean</code> is <code>true</code>,
+     * color samples are assumed to have been multiplied by the alpha sample.
+     * The transparency, <code>trans</code>, specifies what alpha values
+     * can be represented by this color model.
+     * The transfer type is the type of primitive array used to represent
+     * pixel values.
+     * @param space the specified <code>ColorSpace</code>
+     * @param bits the number of bits in the pixel values
+     * @param rmask specifies the mask representing
+     *         the bits of the pixel values that represent the red
+     *         color component
+     * @param gmask specifies the mask representing
+     *         the bits of the pixel values that represent the green
+     *         color component
+     * @param bmask specifies the mask representing
+     *         the bits of the pixel values that represent
+     *         the blue color component
+     * @param amask specifies the mask representing
+     *         the bits of the pixel values that represent
+     *         the alpha component
+     * @param isAlphaPremultiplied <code>true</code> if color samples are
+     *        premultiplied by the alpha sample; <code>false</code> otherwise
+     * @param trans specifies the alpha value that can be represented by
+     *        this color model
+     * @param transferType the type of array used to represent pixel values
+     * @throws IllegalArgumentException if <code>space</code> is not a
+     *         TYPE_RGB space
+     * @see ColorSpace
+     */
+    public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask,
+                            int bmask, int amask,
+                            boolean isAlphaPremultiplied,
+                            int trans, int transferType) {
+        super (bits, PackedColorModel.createBitsArray(rmask, gmask, bmask,
+                                                      amask),
+               space, (amask == 0 ? false : true),
+               isAlphaPremultiplied, trans, transferType);
+
+        if (space.getType() != ColorSpace.TYPE_RGB) {
+            throw new IllegalArgumentException("ColorSpace must be TYPE_RGB.");
+        }
+        maskArray = new int[numComponents];
+        maskOffsets = new int[numComponents];
+        scaleFactors = new float[numComponents];
+
+        DecomposeMask(rmask, 0, "red");
+
+        DecomposeMask(gmask, 1, "green");
+
+        DecomposeMask(bmask, 2, "blue");
+
+        if (amask != 0) {
+            DecomposeMask(amask, 3, "alpha");
+            if (nBits[3] == 1) {
+                transparency = Transparency.BITMASK;
+            }
+        }
+    }
+
+    /**
+     * Returns the mask indicating which bits in a pixel
+     * contain the specified color/alpha sample.  For color
+     * samples, <code>index</code> corresponds to the placement of color
+     * sample names in the color space.  Thus, an <code>index</code>
+     * equal to 0 for a CMYK ColorSpace would correspond to
+     * Cyan and an <code>index</code> equal to 1 would correspond to
+     * Magenta.  If there is alpha, the alpha <code>index</code> would be:
+     * <pre>
+     *      alphaIndex = numComponents() - 1;
+     * </pre>
+     * @param index the specified color or alpha sample
+     * @return the mask, which indicates which bits of the <code>int</code>
+     *         pixel representation contain the color or alpha sample specified
+     *         by <code>index</code>.
+     * @throws ArrayIndexOutOfBoundsException if <code>index</code> is
+     *         greater than the number of components minus 1 in this
+     *         <code>PackedColorModel</code> or if <code>index</code> is
+     *         less than zero
+     */
+    final public int getMask(int index) {
+        return maskArray[index];
+    }
+
+    /**
+     * Returns a mask array indicating which bits in a pixel
+     * contain the color and alpha samples.
+     * @return the mask array , which indicates which bits of the
+     *         <code>int</code> pixel
+     *         representation contain the color or alpha samples.
+     */
+    final public int[] getMasks() {
+        return maskArray.clone();
+    }
+
+    /*
+     * A utility function to compute the mask offset and scalefactor,
+     * store these and the mask in instance arrays, and verify that
+     * the mask fits in the specified pixel size.
+     */
+    private void DecomposeMask(int mask,  int idx, String componentName) {
+        int off = 0;
+        int count = nBits[idx];
+
+        // Store the mask
+        maskArray[idx]   = mask;
+
+        // Now find the shift
+        if (mask != 0) {
+            while ((mask & 1) == 0) {
+                mask >>>= 1;
+                off++;
+            }
+        }
+
+        if (off + count > pixel_bits) {
+            throw new IllegalArgumentException(componentName + " mask "+
+                                        Integer.toHexString(maskArray[idx])+
+                                               " overflows pixel (expecting "+
+                                               pixel_bits+" bits");
+        }
+
+        maskOffsets[idx] = off;
+        if (count == 0) {
+            // High enough to scale any 0-ff value down to 0.0, but not
+            // high enough to get Infinity when scaling back to pixel bits
+            scaleFactors[idx] = 256.0f;
+        } else {
+            scaleFactors[idx] = 255.0f / ((1 << count) - 1);
+        }
+
+    }
+
+    /**
+     * Creates a <code>SampleModel</code> with the specified width and
+     * height that has a data layout compatible with this
+     * <code>ColorModel</code>.
+     * @param w the width (in pixels) of the region of the image data
+     *          described
+     * @param h the height (in pixels) of the region of the image data
+     *          described
+     * @return the newly created <code>SampleModel</code>.
+     * @throws IllegalArgumentException if <code>w</code> or
+     *         <code>h</code> is not greater than 0
+     * @see SampleModel
+     */
+    public SampleModel createCompatibleSampleModel(int w, int h) {
+        return new SinglePixelPackedSampleModel(transferType, w, h,
+                                                maskArray);
+    }
+
+    /**
+     * Checks if the specified <code>SampleModel</code> is compatible
+     * with this <code>ColorModel</code>.  If <code>sm</code> is
+     * <code>null</code>, this method returns <code>false</code>.
+     * @param sm the specified <code>SampleModel</code>,
+     * or <code>null</code>
+     * @return <code>true</code> if the specified <code>SampleModel</code>
+     *         is compatible with this <code>ColorModel</code>;
+     *         <code>false</code> otherwise.
+     * @see SampleModel
+     */
+    public boolean isCompatibleSampleModel(SampleModel sm) {
+        if (! (sm instanceof SinglePixelPackedSampleModel)) {
+            return false;
+        }
+
+        // Must have the same number of components
+        if (numComponents != sm.getNumBands()) {
+            return false;
+        }
+
+        // Transfer type must be the same
+        if (sm.getTransferType() != transferType) {
+            return false;
+        }
+
+        SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
+        // Now compare the specific masks
+        int[] bitMasks = sppsm.getBitMasks();
+        if (bitMasks.length != maskArray.length) {
+            return false;
+        }
+
+        /* compare 'effective' masks only, i.e. only part of the mask
+         * which fits the capacity of the transfer type.
+         */
+        int maxMask = (int)((1L << DataBuffer.getDataTypeSize(transferType)) - 1);
+        for (int i=0; i < bitMasks.length; i++) {
+            if ((maxMask & bitMasks[i]) != (maxMask & maskArray[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a {@link WritableRaster} representing the alpha channel of
+     * an image, extracted from the input <code>WritableRaster</code>.
+     * This method assumes that <code>WritableRaster</code> objects
+     * associated with this <code>ColorModel</code> store the alpha band,
+     * if present, as the last band of image data.  Returns <code>null</code>
+     * if there is no separate spatial alpha channel associated with this
+     * <code>ColorModel</code>.  This method creates a new
+     * <code>WritableRaster</code>, but shares the data array.
+     * @param raster a <code>WritableRaster</code> containing an image
+     * @return a <code>WritableRaster</code> that represents the alpha
+     *         channel of the image contained in <code>raster</code>.
+     */
+    public WritableRaster getAlphaRaster(WritableRaster raster) {
+        if (hasAlpha() == false) {
+            return null;
+        }
+
+        int x = raster.getMinX();
+        int y = raster.getMinY();
+        int[] band = new int[1];
+        band[0] = raster.getNumBands() - 1;
+        return raster.createWritableChild(x, y, raster.getWidth(),
+                                          raster.getHeight(), x, y,
+                                          band);
+    }
+
+    /**
+     * Tests if the specified <code>Object</code> is an instance
+     * of <code>PackedColorModel</code> and equals this
+     * <code>PackedColorModel</code>.
+     * @param obj the <code>Object</code> to test for equality
+     * @return <code>true</code> if the specified <code>Object</code>
+     * is an instance of <code>PackedColorModel</code> and equals this
+     * <code>PackedColorModel</code>; <code>false</code> otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (!(obj instanceof PackedColorModel)) {
+            return false;
+        }
+
+        if (!super.equals(obj)) {
+            return false;
+        }
+
+        PackedColorModel cm = (PackedColorModel) obj;
+        int numC = cm.getNumComponents();
+        if (numC != numComponents) {
+            return false;
+        }
+        for(int i=0; i < numC; i++) {
+            if (maskArray[i] != cm.getMask(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private final static int[] createBitsArray(int[]colorMaskArray,
+                                               int alphaMask) {
+        int numColors = colorMaskArray.length;
+        int numAlpha = (alphaMask == 0 ? 0 : 1);
+        int[] arr = new int[numColors+numAlpha];
+        for (int i=0; i < numColors; i++) {
+            arr[i] = countBits(colorMaskArray[i]);
+            if (arr[i] < 0) {
+                throw new IllegalArgumentException("Noncontiguous color mask ("
+                                     + Integer.toHexString(colorMaskArray[i])+
+                                     "at index "+i);
+            }
+        }
+        if (alphaMask != 0) {
+            arr[numColors] = countBits(alphaMask);
+            if (arr[numColors] < 0) {
+                throw new IllegalArgumentException("Noncontiguous alpha mask ("
+                                     + Integer.toHexString(alphaMask));
+            }
+        }
+        return arr;
+    }
+
+    private final static int[] createBitsArray(int rmask, int gmask, int bmask,
+                                         int amask) {
+        int[] arr = new int[3 + (amask == 0 ? 0 : 1)];
+        arr[0] = countBits(rmask);
+        arr[1] = countBits(gmask);
+        arr[2] = countBits(bmask);
+        if (arr[0] < 0) {
+            throw new IllegalArgumentException("Noncontiguous red mask ("
+                                     + Integer.toHexString(rmask));
+        }
+        else if (arr[1] < 0) {
+            throw new IllegalArgumentException("Noncontiguous green mask ("
+                                     + Integer.toHexString(gmask));
+        }
+        else if (arr[2] < 0) {
+            throw new IllegalArgumentException("Noncontiguous blue mask ("
+                                     + Integer.toHexString(bmask));
+        }
+        if (amask != 0) {
+            arr[3] = countBits(amask);
+            if (arr[3] < 0) {
+                throw new IllegalArgumentException("Noncontiguous alpha mask ("
+                                     + Integer.toHexString(amask));
+            }
+        }
+        return arr;
+    }
+
+    private final static int countBits(int mask) {
+        int count = 0;
+        if (mask != 0) {
+            while ((mask & 1) == 0) {
+                mask >>>= 1;
+            }
+            while ((mask & 1) == 1) {
+                mask >>>= 1;
+                count++;
+            }
+        }
+        if (mask != 0) {
+            return -1;
+        }
+        return count;
+    }
+
+}