blob: ad852876a33be9d9a136e412d70ae57b0df57fc6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2001 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt.image;
27
28import java.awt.Transparency;
29import 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
87public 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 (int[]) 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 for (int i=0; i < bitMasks.length; i++) {
347 if (bitMasks[i] != maskArray[i]) {
348 return false;
349 }
350 }
351
352 return true;
353 }
354
355 /**
356 * Returns a {@link WritableRaster} representing the alpha channel of
357 * an image, extracted from the input <code>WritableRaster</code>.
358 * This method assumes that <code>WritableRaster</code> objects
359 * associated with this <code>ColorModel</code> store the alpha band,
360 * if present, as the last band of image data. Returns <code>null</code>
361 * if there is no separate spatial alpha channel associated with this
362 * <code>ColorModel</code>. This method creates a new
363 * <code>WritableRaster</code>, but shares the data array.
364 * @param raster a <code>WritableRaster</code> containing an image
365 * @return a <code>WritableRaster</code> that represents the alpha
366 * channel of the image contained in <code>raster</code>.
367 */
368 public WritableRaster getAlphaRaster(WritableRaster raster) {
369 if (hasAlpha() == false) {
370 return null;
371 }
372
373 int x = raster.getMinX();
374 int y = raster.getMinY();
375 int[] band = new int[1];
376 band[0] = raster.getNumBands() - 1;
377 return raster.createWritableChild(x, y, raster.getWidth(),
378 raster.getHeight(), x, y,
379 band);
380 }
381
382 /**
383 * Tests if the specified <code>Object</code> is an instance
384 * of <code>PackedColorModel</code> and equals this
385 * <code>PackedColorModel</code>.
386 * @param obj the <code>Object</code> to test for equality
387 * @return <code>true</code> if the specified <code>Object</code>
388 * is an instance of <code>PackedColorModel</code> and equals this
389 * <code>PackedColorModel</code>; <code>false</code> otherwise.
390 */
391 public boolean equals(Object obj) {
392 if (!(obj instanceof PackedColorModel)) {
393 return false;
394 }
395
396 if (!super.equals(obj)) {
397 return false;
398 }
399
400 PackedColorModel cm = (PackedColorModel) obj;
401 int numC = cm.getNumComponents();
402 if (numC != numComponents) {
403 return false;
404 }
405 for(int i=0; i < numC; i++) {
406 if (maskArray[i] != cm.getMask(i)) {
407 return false;
408 }
409 }
410 return true;
411 }
412
413 private final static int[] createBitsArray(int[]colorMaskArray,
414 int alphaMask) {
415 int numColors = colorMaskArray.length;
416 int numAlpha = (alphaMask == 0 ? 0 : 1);
417 int[] arr = new int[numColors+numAlpha];
418 for (int i=0; i < numColors; i++) {
419 arr[i] = countBits(colorMaskArray[i]);
420 if (arr[i] < 0) {
421 throw new IllegalArgumentException("Noncontiguous color mask ("
422 + Integer.toHexString(colorMaskArray[i])+
423 "at index "+i);
424 }
425 }
426 if (alphaMask != 0) {
427 arr[numColors] = countBits(alphaMask);
428 if (arr[numColors] < 0) {
429 throw new IllegalArgumentException("Noncontiguous alpha mask ("
430 + Integer.toHexString(alphaMask));
431 }
432 }
433 return arr;
434 }
435
436 private final static int[] createBitsArray(int rmask, int gmask, int bmask,
437 int amask) {
438 int[] arr = new int[3 + (amask == 0 ? 0 : 1)];
439 arr[0] = countBits(rmask);
440 arr[1] = countBits(gmask);
441 arr[2] = countBits(bmask);
442 if (arr[0] < 0) {
443 throw new IllegalArgumentException("Noncontiguous red mask ("
444 + Integer.toHexString(rmask));
445 }
446 else if (arr[1] < 0) {
447 throw new IllegalArgumentException("Noncontiguous green mask ("
448 + Integer.toHexString(gmask));
449 }
450 else if (arr[2] < 0) {
451 throw new IllegalArgumentException("Noncontiguous blue mask ("
452 + Integer.toHexString(bmask));
453 }
454 if (amask != 0) {
455 arr[3] = countBits(amask);
456 if (arr[3] < 0) {
457 throw new IllegalArgumentException("Noncontiguous alpha mask ("
458 + Integer.toHexString(amask));
459 }
460 }
461 return arr;
462 }
463
464 private final static int countBits(int mask) {
465 int count = 0;
466 if (mask != 0) {
467 while ((mask & 1) == 0) {
468 mask >>>= 1;
469 }
470 while ((mask & 1) == 1) {
471 mask >>>= 1;
472 count++;
473 }
474 }
475 if (mask != 0) {
476 return -1;
477 }
478 return count;
479 }
480
481}