1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Igor V. Stolyarov
19 * @version $Revision$
20 */
21
22package java.awt.image;
23
24import java.awt.Transparency;
25import java.awt.color.ColorSpace;
26import java.util.Arrays;
27
28import org.apache.harmony.awt.internal.nls.Messages;
29
30/**
31 * The class PackedColorModel represents a color model where the components are
32 * just the red, green, and blue bands, plus an alpha band if alpha is
33 * supported.
34 *
35 * @since Android 1.0
36 */
37public abstract class PackedColorModel extends ColorModel {
38
39    /**
40     * The component masks.
41     */
42    int componentMasks[];
43
44    /**
45     * The offsets.
46     */
47    int offsets[];
48
49    /**
50     * The scales.
51     */
52    float scales[];
53
54    /**
55     * Instantiates a new packed color model.
56     *
57     * @param space
58     *            the color space.
59     * @param bits
60     *            the array of component masks.
61     * @param colorMaskArray
62     *            the array that gives the bitmask corresponding to each color
63     *            band (red, green, and blue).
64     * @param alphaMask
65     *            the bitmask corresponding to the alpha band.
66     * @param isAlphaPremultiplied
67     *            whether the alpha is pre-multiplied in this color model.
68     * @param trans
69     *            the transparency strategy, @see java.awt.Transparency.
70     * @param transferType
71     *            the transfer type (primitive java type to use for the
72     *            components).
73     * @throws IllegalArgumentException
74     *             if the number of bits in the combined bitmasks for the color
75     *             bands is less than one or greater than 32.
76     */
77    public PackedColorModel(ColorSpace space, int bits, int colorMaskArray[], int alphaMask,
78            boolean isAlphaPremultiplied, int trans, int transferType) {
79
80        super(bits, createBits(colorMaskArray, alphaMask), space, (alphaMask == 0 ? false : true),
81                isAlphaPremultiplied, trans, validateTransferType(transferType));
82
83        if (pixel_bits < 1 || pixel_bits > 32) {
84            // awt.236=The bits is less than 1 or greater than 32
85            throw new IllegalArgumentException(Messages.getString("awt.236")); //$NON-NLS-1$
86        }
87
88        componentMasks = new int[numComponents];
89        for (int i = 0; i < numColorComponents; i++) {
90            componentMasks[i] = colorMaskArray[i];
91        }
92
93        if (hasAlpha) {
94            componentMasks[numColorComponents] = alphaMask;
95            if (this.bits[numColorComponents] == 1) {
96                transparency = Transparency.BITMASK;
97            }
98        }
99
100        parseComponents();
101    }
102
103    /**
104     * Instantiates a new packed color model.
105     *
106     * @param space
107     *            the color space.
108     * @param bits
109     *            the array of component masks.
110     * @param rmask
111     *            the bitmask corresponding to the red band.
112     * @param gmask
113     *            the bitmask corresponding to the green band.
114     * @param bmask
115     *            the bitmask corresponding to the blue band.
116     * @param amask
117     *            the bitmask corresponding to the alpha band.
118     * @param isAlphaPremultiplied
119     *            whether the alpha is pre-multiplied in this color model.
120     * @param trans
121     *            the transparency strategy, @see java.awt.Transparency.
122     * @param transferType
123     *            the transfer type (primitive java type to use for the
124     *            components).
125     * @throws IllegalArgumentException
126     *             if the number of bits in the combined bitmasks for the color
127     *             bands is less than one or greater than 32.
128     */
129    public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask, int bmask, int amask,
130            boolean isAlphaPremultiplied, int trans, int transferType) {
131
132        super(bits, createBits(rmask, gmask, bmask, amask), space, (amask == 0 ? false : true),
133                isAlphaPremultiplied, trans, validateTransferType(transferType));
134
135        if (pixel_bits < 1 || pixel_bits > 32) {
136            // awt.236=The bits is less than 1 or greater than 32
137            throw new IllegalArgumentException(Messages.getString("awt.236")); //$NON-NLS-1$
138        }
139
140        if (cs.getType() != ColorSpace.TYPE_RGB) {
141            // awt.239=The space is not a TYPE_RGB space
142            throw new IllegalArgumentException(Messages.getString("awt.239")); //$NON-NLS-1$
143        }
144
145        for (int i = 0; i < numColorComponents; i++) {
146            if (cs.getMinValue(i) != 0.0f || cs.getMaxValue(i) != 1.0f) {
147                // awt.23A=The min/max normalized component values are not
148                // 0.0/1.0
149                throw new IllegalArgumentException(Messages.getString("awt.23A")); //$NON-NLS-1$
150            }
151        }
152        componentMasks = new int[numComponents];
153        componentMasks[0] = rmask;
154        componentMasks[1] = gmask;
155        componentMasks[2] = bmask;
156
157        if (hasAlpha) {
158            componentMasks[3] = amask;
159            if (this.bits[3] == 1) {
160                transparency = Transparency.BITMASK;
161            }
162        }
163
164        parseComponents();
165    }
166
167    @Override
168    public WritableRaster getAlphaRaster(WritableRaster raster) {
169        if (!hasAlpha) {
170            return null;
171        }
172
173        int x = raster.getMinX();
174        int y = raster.getMinY();
175        int w = raster.getWidth();
176        int h = raster.getHeight();
177        int band[] = new int[1];
178        band[0] = raster.getNumBands() - 1;
179        return raster.createWritableChild(x, y, w, h, x, y, band);
180    }
181
182    @Override
183    public boolean equals(Object obj) {
184        if (obj == null) {
185            return false;
186        }
187        if (!(obj instanceof PackedColorModel)) {
188            return false;
189        }
190        PackedColorModel cm = (PackedColorModel)obj;
191
192        return (pixel_bits == cm.getPixelSize() && transferType == cm.getTransferType()
193                && cs.getType() == cm.getColorSpace().getType() && hasAlpha == cm.hasAlpha()
194                && isAlphaPremultiplied == cm.isAlphaPremultiplied()
195                && transparency == cm.getTransparency()
196                && numColorComponents == cm.getNumColorComponents()
197                && numComponents == cm.getNumComponents()
198                && Arrays.equals(bits, cm.getComponentSize()) && Arrays.equals(componentMasks, cm
199                .getMasks()));
200    }
201
202    @Override
203    public boolean isCompatibleSampleModel(SampleModel sm) {
204        if (sm == null) {
205            return false;
206        }
207        if (!(sm instanceof SinglePixelPackedSampleModel)) {
208            return false;
209        }
210        SinglePixelPackedSampleModel esm = (SinglePixelPackedSampleModel)sm;
211
212        return ((esm.getNumBands() == numComponents) && (esm.getTransferType() == transferType) && Arrays
213                .equals(esm.getBitMasks(), componentMasks));
214    }
215
216    @Override
217    public SampleModel createCompatibleSampleModel(int w, int h) {
218        return new SinglePixelPackedSampleModel(transferType, w, h, componentMasks);
219    }
220
221    /**
222     * Gets the bitmask corresponding to the specified color component.
223     *
224     * @param index
225     *            the index of the desired color.
226     * @return the mask.
227     */
228    public final int getMask(int index) {
229        return componentMasks[index];
230    }
231
232    /**
233     * Gets the bitmasks of the components.
234     *
235     * @return the masks.
236     */
237    public final int[] getMasks() {
238        return (componentMasks.clone());
239    }
240
241    /**
242     * Creates the bits.
243     *
244     * @param colorMaskArray
245     *            the color mask array.
246     * @param alphaMask
247     *            the alpha mask.
248     * @return the int[].
249     */
250    private static int[] createBits(int colorMaskArray[], int alphaMask) {
251        int bits[];
252        int numComp;
253        if (alphaMask == 0) {
254            numComp = colorMaskArray.length;
255        } else {
256            numComp = colorMaskArray.length + 1;
257        }
258
259        bits = new int[numComp];
260        int i = 0;
261        for (; i < colorMaskArray.length; i++) {
262            bits[i] = countCompBits(colorMaskArray[i]);
263            if (bits[i] < 0) {
264                // awt.23B=The mask of the {0} component is not contiguous
265                throw new IllegalArgumentException(Messages.getString("awt.23B", i)); //$NON-NLS-1$
266            }
267        }
268
269        if (i < numComp) {
270            bits[i] = countCompBits(alphaMask);
271
272            if (bits[i] < 0) {
273                // awt.23C=The mask of the alpha component is not contiguous
274                throw new IllegalArgumentException(Messages.getString("awt.23C")); //$NON-NLS-1$
275            }
276        }
277
278        return bits;
279    }
280
281    /**
282     * Creates the bits.
283     *
284     * @param rmask
285     *            the rmask.
286     * @param gmask
287     *            the gmask.
288     * @param bmask
289     *            the bmask.
290     * @param amask
291     *            the amask.
292     * @return the int[].
293     */
294    private static int[] createBits(int rmask, int gmask, int bmask, int amask) {
295
296        int numComp;
297        if (amask == 0) {
298            numComp = 3;
299        } else {
300            numComp = 4;
301        }
302        int bits[] = new int[numComp];
303
304        bits[0] = countCompBits(rmask);
305        if (bits[0] < 0) {
306            // awt.23D=The mask of the red component is not contiguous
307            throw new IllegalArgumentException(Messages.getString("awt.23D")); //$NON-NLS-1$
308        }
309
310        bits[1] = countCompBits(gmask);
311        if (bits[1] < 0) {
312            // awt.23E=The mask of the green component is not contiguous
313            throw new IllegalArgumentException(Messages.getString("awt.23E")); //$NON-NLS-1$
314        }
315
316        bits[2] = countCompBits(bmask);
317        if (bits[2] < 0) {
318            // awt.23F=The mask of the blue component is not contiguous
319            throw new IllegalArgumentException(Messages.getString("awt.23F")); //$NON-NLS-1$
320        }
321
322        if (amask != 0) {
323            bits[3] = countCompBits(amask);
324            if (bits[3] < 0) {
325                // awt.23C=The mask of the alpha component is not contiguous
326                throw new IllegalArgumentException(Messages.getString("awt.23C")); //$NON-NLS-1$
327            }
328        }
329
330        return bits;
331    }
332
333    /**
334     * Count comp bits.
335     *
336     * @param compMask
337     *            the comp mask.
338     * @return the int.
339     */
340    private static int countCompBits(int compMask) {
341        int bits = 0;
342        if (compMask != 0) {
343            // Deleting final zeros
344            while ((compMask & 1) == 0) {
345                compMask >>>= 1;
346            }
347            // Counting component bits
348            while ((compMask & 1) == 1) {
349                compMask >>>= 1;
350                bits++;
351            }
352        }
353
354        if (compMask != 0) {
355            return -1;
356        }
357
358        return bits;
359    }
360
361    /**
362     * Validate transfer type.
363     *
364     * @param transferType
365     *            the transfer type.
366     * @return the int.
367     */
368    private static int validateTransferType(int transferType) {
369        if (transferType != DataBuffer.TYPE_BYTE && transferType != DataBuffer.TYPE_USHORT
370                && transferType != DataBuffer.TYPE_INT) {
371            // awt.240=The transferType not is one of DataBuffer.TYPE_BYTE,
372            // DataBuffer.TYPE_USHORT or DataBuffer.TYPE_INT
373            throw new IllegalArgumentException(Messages.getString("awt.240")); //$NON-NLS-1$
374        }
375        return transferType;
376    }
377
378    /**
379     * Parses the components.
380     */
381    private void parseComponents() {
382        offsets = new int[numComponents];
383        scales = new float[numComponents];
384        for (int i = 0; i < numComponents; i++) {
385            int off = 0;
386            int mask = componentMasks[i];
387            while ((mask & 1) == 0) {
388                mask >>>= 1;
389                off++;
390            }
391            offsets[i] = off;
392            if (bits[i] == 0) {
393                scales[i] = 256.0f; // May be any value different from zero,
394                // because will dividing by zero
395            } else {
396                scales[i] = 255.0f / maxValues[i];
397            }
398        }
399
400    }
401
402}
403