1 /* IndexColorModel.java -- Java class for interpreting Pixel objects
2 Copyright (C) 1999, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package java
.awt
.image
;
40 import gnu
.java
.awt
.Buffers
;
42 import java
.awt
.color
.ColorSpace
;
43 import java
.math
.BigInteger
;
46 * Color model similar to pseudo visual in X11.
48 * This color model maps linear pixel values to actual RGB and alpha colors.
49 * Thus, pixel values are indexes into the color map. Each color component is
50 * an 8-bit unsigned value.
52 * The <code>IndexColorModel</code> supports a map of valid pixels, allowing
53 * the representation of holes in the the color map. The valid map is
54 * represented as a {@link BigInteger} where each bit indicates the validity
55 * of the map entry with the same index.
57 * Colors can have alpha components for transparency support. If alpha
58 * component values aren't given, color values are opaque. The model also
59 * supports a reserved pixel value to represent completely transparent colors,
60 * no matter what the actual color component values are.
62 * <code>IndexColorModel</code> supports anywhere from 1 to 16 bit index
63 * values. The allowed transfer types are {@link DataBuffer#TYPE_BYTE} and
64 * {@link DataBuffer#TYPE_USHORT}.
66 * @author C. Brian Jones (cbj@gnu.org)
68 public class IndexColorModel
extends ColorModel
71 private boolean opaque
; // no alpha, but doesn't account for trans
72 private int trans
= -1;
74 private BigInteger validBits
= BigInteger
.ZERO
;
77 * Creates a new indexed color model for <code>size</code> color elements
78 * with no alpha component. Each array must contain at least
79 * <code>size</code> elements. For each array, the i-th color is described
80 * by reds[i], greens[i] and blues[i].
82 * @param bits the number of bits needed to represent <code>size</code>
84 * @param size the number of colors in the color map.
85 * @param reds the red component of all colors.
86 * @param greens the green component of all colors.
87 * @param blues the blue component of all colors.
89 * @throws IllegalArgumentException if <code>bits</code> < 1 or
90 * <code>bits</code> > 16.
91 * @throws NullPointerException if any of the arrays is <code>null</code>.
92 * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater
93 * than the length of the component arrays.
95 public IndexColorModel(int bits
, int size
, byte[] reds
, byte[] greens
,
98 this(bits
, size
, reds
, greens
, blues
, (byte[]) null);
102 * Creates a new indexed color model for <code>size</code> color elements.
103 * Each array must contain at least <code>size</code> elements. For each
104 * array, the i-th color is described by reds[i], greens[i] and blues[i].
105 * All the colors are opaque except for the transparent color.
107 * @param bits the number of bits needed to represent <code>size</code>
109 * @param size the number of colors in the color map
110 * @param reds the red component of all colors
111 * @param greens the green component of all colors
112 * @param blues the blue component of all colors
113 * @param trans the index of the transparent color (use -1 for no
114 * transparent color).
116 * @throws IllegalArgumentException if <code>bits</code> < 1 or
117 * <code>bits</code> > 16.
118 * @throws NullPointerException if any of the arrays is <code>null</code>.
119 * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater
120 * than the length of the component arrays.
122 public IndexColorModel(int bits
, int size
, byte[] reds
, byte[] greens
,
123 byte[] blues
, int trans
)
125 super(bits
, nArray(8, (0 <= trans
&& trans
< size
) ?
4 : 3),
126 ColorSpace
.getInstance(ColorSpace
.CS_sRGB
),
127 (0 <= trans
&& trans
< size
), // hasAlpha
129 Buffers
.smallestAppropriateTransferType(bits
));
131 throw new IllegalArgumentException("bits < 1");
133 throw new IllegalArgumentException("bits > 16");
135 throw new IllegalArgumentException("size < 1");
137 if (0 <= trans
&& trans
< size
) {
139 transparency
= BITMASK
;
142 for (int i
= 0; i
< size
; i
++)
145 | ((reds
[i
] & 0xff) << 16)
146 | ((greens
[i
] & 0xff) << 8)
147 | (blues
[i
] & 0xff));
149 // Generate a bigint with 1's for every pixel
150 validBits
= validBits
.setBit(size
).subtract(BigInteger
.ONE
);
154 * Creates a new indexed color model for <code>size</code> color elements
155 * including alpha. Each array must contain at least <code>size</code>
156 * elements. For each array, the i-th color is described
157 * by reds[i], greens[i], blues[i] and alphas[i].
159 * @param bits the number of bits needed to represent <code>size</code>
161 * @param size the number of colors in the color map.
162 * @param reds the red component of all colors.
163 * @param greens the green component of all colors.
164 * @param blues the blue component of all colors.
165 * @param alphas the alpha component of all colors (<code>null</code>
168 * @throws IllegalArgumentException if <code>bits</code> < 1 or
169 * <code>bits</code> > 16.
170 * @throws NullPointerException if <code>reds</code>, <code>greens</code> or
171 * <code>blues</code> is <code>null</code>.
172 * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater
173 * than the length of the component arrays.
175 public IndexColorModel(int bits
, int size
, byte[] reds
, byte[] greens
,
176 byte[] blues
, byte[] alphas
)
178 super(bits
, nArray(8, (alphas
== null ?
3 : 4)),
179 ColorSpace
.getInstance(ColorSpace
.CS_sRGB
),
180 (alphas
!= null), false, TRANSLUCENT
,
181 Buffers
.smallestAppropriateTransferType(bits
));
183 throw new IllegalArgumentException("bits < 1");
185 throw new IllegalArgumentException("bits > 16");
187 throw new IllegalArgumentException("size < 1");
189 opaque
= (alphas
== null);
194 for (int i
= 0; i
< size
; i
++)
197 | ((reds
[i
] & 0xff) << 16)
198 | ((greens
[i
] & 0xff) << 8)
199 | (blues
[i
] & 0xff));
201 transparency
= OPAQUE
;
205 byte alphaZero
= (byte) 0x00;
206 byte alphaOne
= (byte) 0xFF;
207 for (int i
= 0; i
< size
; i
++)
209 alphaZero
= (byte) (alphaZero
| alphas
[i
]);
210 alphaOne
= (byte) (alphaOne
& alphas
[i
]);
211 rgb
[i
] = ((alphas
[i
] & 0xff) << 24
212 | ((reds
[i
] & 0xff) << 16)
213 | ((greens
[i
] & 0xff) << 8)
214 | (blues
[i
] & 0xff));
216 if ((alphaZero
== (byte) 0x00) || (alphaOne
== (byte) 0xFF))
217 transparency
= BITMASK
;
219 transparency
= TRANSLUCENT
;
222 // Generate a bigint with 1's for every pixel
223 validBits
= validBits
.setBit(size
).subtract(BigInteger
.ONE
);
227 * Creates a new indexed color model using the color components in
228 * <code>cmap</code>. If <code>hasAlpha</code> is <code>true</code> then
229 * <code>cmap</code> contains an alpha component after each of the red, green
230 * and blue components.
232 * @param bits the number of bits needed to represent <code>size</code>
234 * @param size the number of colors in the color map
235 * @param cmap packed color components
236 * @param start the offset of the first color component in <code>cmap</code>
237 * @param hasAlpha <code>cmap</code> has alpha values
238 * @throws IllegalArgumentException if bits < 1, bits > 16, or size
240 * @throws NullPointerException if <code>cmap</code> is <code>null</code>.
242 public IndexColorModel(int bits
, int size
, byte[] cmap
, int start
,
245 this(bits
, size
, cmap
, start
, hasAlpha
, -1);
249 * Construct an IndexColorModel from an array of red, green, blue, and
250 * optional alpha components. The component values are interleaved as RGB(A).
252 * @param bits the number of bits needed to represent <code>size</code>
254 * @param size the number of colors in the color map
255 * @param cmap interleaved color components
256 * @param start the offset of the first color component in <code>cmap</code>
257 * @param hasAlpha <code>cmap</code> has alpha values
258 * @param trans the index of the transparent color
259 * @throws IllegalArgumentException if bits < 1, bits > 16, or size
261 * @throws NullPointerException if <code>cmap</code> is <code>null</code>.
263 public IndexColorModel(int bits
, int size
, byte[] cmap
, int start
,
264 boolean hasAlpha
, int trans
)
266 super(bits
, nArray(8, hasAlpha
|| (0 <= trans
&& trans
< size
) ?
4 : 3),
267 ColorSpace
.getInstance(ColorSpace
.CS_sRGB
),
268 hasAlpha
|| (0 <= trans
&& trans
< size
), false, OPAQUE
,
269 Buffers
.smallestAppropriateTransferType(bits
));
271 throw new IllegalArgumentException("bits < 1");
273 throw new IllegalArgumentException("bits > 16");
275 throw new IllegalArgumentException("size < 1");
278 if (0 <= trans
&& trans
< size
)
285 int alphaZero
= 0x00; // use to detect all zeros
286 int alphaOne
= 0xff; // use to detect all ones
287 for (int i
= 0; i
< size
; i
++) {
288 alpha
= cmap
[4 * i
+ 3 + start
] & 0xff;
289 alphaZero
= alphaZero
| alpha
;
290 alphaOne
= alphaOne
& alpha
;
294 | ((cmap
[4 * i
+ start
] & 0xff) << 16)
296 | ((cmap
[4 * i
+ 1 + start
] & 0xff) << 8)
298 | (cmap
[4 * i
+ 2 + start
] & 0xff));
301 transparency
= BITMASK
;
302 else if (alphaOne
== 255)
303 transparency
= (trans
!= -1 ? BITMASK
: OPAQUE
);
305 transparency
= TRANSLUCENT
;
309 for (int i
= 0; i
< size
; i
++)
312 | ((cmap
[3 * i
+ start
] & 0xff) << 16)
314 | ((cmap
[3 * i
+ 1 + start
] & 0xff) << 8)
316 | (cmap
[3 * i
+ 2 + start
] & 0xff));
318 transparency
= BITMASK
;
321 // Generate a bigint with 1's for every pixel
322 validBits
= validBits
.setBit(size
).subtract(BigInteger
.ONE
);
326 * Construct an IndexColorModel from an array of <code>size</code> packed
327 * colors. Each int element contains 8-bit red, green, blue, and optional
328 * alpha values packed in order. If hasAlpha is false, then all the colors
329 * are opaque except for the transparent color.
331 * @param bits the number of bits needed to represent <code>size</code>
333 * @param size the number of colors in the color map
334 * @param cmap packed color components
335 * @param start the offset of the first color component in <code>cmap</code>
336 * @param hasAlpha <code>cmap</code> has alpha values
337 * @param trans the index of the transparent color
338 * @param transferType {@link DataBuffer#TYPE_BYTE} or
339 {@link DataBuffer#TYPE_USHORT}.
340 * @throws IllegalArgumentException if bits < 1, bits > 16, or size
342 * @throws IllegalArgumentException if <code>transferType</code> is something
343 * other than {@link DataBuffer#TYPE_BYTE} or
344 * {@link DataBuffer#TYPE_USHORT}.
346 public IndexColorModel(int bits
, int size
, int[] cmap
, int start
,
347 boolean hasAlpha
, int trans
, int transferType
)
350 nArray(8, 4), // bits for each channel
351 ColorSpace
.getInstance(ColorSpace
.CS_sRGB
), // sRGB
353 false, // not premultiplied
354 TRANSLUCENT
, transferType
);
355 if (transferType
!= DataBuffer
.TYPE_BYTE
356 && transferType
!= DataBuffer
.TYPE_USHORT
)
357 throw new IllegalArgumentException();
359 throw new IllegalArgumentException("bits > 16");
361 throw new IllegalArgumentException("size < 1");
364 if (0 <= trans
&& trans
< size
)
369 for (int i
= 0; i
< size
; i
++)
370 rgb
[i
] = cmap
[i
+ start
] | 0xff000000;
372 System
.arraycopy(cmap
, start
, rgb
, 0, size
);
374 // Generate a bigint with 1's for every pixel
375 validBits
= validBits
.setBit(size
).subtract(BigInteger
.ONE
);
379 * Construct an IndexColorModel using a colormap with holes.
381 * The IndexColorModel is built from the array of ints defining the
382 * colormap. Each element contains red, green, blue, and alpha
383 * components. The ColorSpace is sRGB. The transparency value is
384 * automatically determined.
386 * This constructor permits indicating which colormap entries are valid,
387 * using the validBits argument. Each entry in cmap is valid if the
388 * corresponding bit in validBits is set.
390 * @param bits the number of bits needed to represent <code>size</code>
392 * @param size the number of colors in the color map.
393 * @param cmap packed color components.
394 * @param start the offset of the first color component in <code>cmap</code>.
395 * @param transferType {@link DataBuffer#TYPE_BYTE} or
396 * {@link DataBuffer#TYPE_USHORT}.
397 * @param validBits a map of the valid entries in <code>cmap</code>.
398 * @throws IllegalArgumentException if bits < 1, bits > 16, or size
400 * @throws IllegalArgumentException if transferType is something other than
401 * {@link DataBuffer#TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT}.
403 public IndexColorModel(int bits
, int size
, int[] cmap
, int start
,
404 int transferType
, BigInteger validBits
)
406 super(bits
, // total bits, sRGB, four channels
407 nArray(8, 4), // bits for each channel
408 ColorSpace
.getInstance(ColorSpace
.CS_sRGB
), // sRGB
410 false, // not premultiplied
411 TRANSLUCENT
, transferType
);
412 if (transferType
!= DataBuffer
.TYPE_BYTE
413 && transferType
!= DataBuffer
.TYPE_USHORT
)
414 throw new IllegalArgumentException();
416 throw new IllegalArgumentException("bits > 16");
418 throw new IllegalArgumentException("size < 1");
422 this.validBits
= validBits
;
426 for (int i
= 0; i
< size
; i
++)
427 rgb
[i
] = cmap
[i
+ start
] | 0xff000000;
429 System
.arraycopy(cmap
, start
, rgb
, 0, size
);
433 * Returns the size of the color lookup table.
435 * @return The size of the color lookup table.
437 public final int getMapSize()
443 * Get the index of the transparent color in this color model.
445 * @return The index of the color that is considered transparent, or -1 if
446 * there is no transparent color.
448 public final int getTransparentPixel()
454 * Fills the supplied array with the red component of each color in the
457 * @param r an array that is at least as large as {@link #getMapSize()}.
458 * @throws NullPointerException if <code>r</code> is <code>null</code>.
459 * @throws ArrayIndexOutOfBoundsException if <code>r</code> has less
460 * than {@link #getMapSize()} elements.
462 public final void getReds(byte[] r
)
465 for (i
= 0; i
< map_size
; i
++)
466 r
[i
] = (byte) ((0x00FF0000 & rgb
[i
]) >> 16);
470 * Fills the supplied array with the green component of each color in the
473 * @param g an array that is at least as large as {@link #getMapSize()}.
474 * @throws NullPointerException if <code>g</code> is <code>null</code>.
475 * @throws ArrayIndexOutOfBoundsException if <code>g</code> has less
476 * than {@link #getMapSize()} elements.
478 public final void getGreens(byte[] g
)
481 for (i
= 0; i
< map_size
; i
++)
482 g
[i
] = (byte) ((0x0000FF00 & rgb
[i
]) >> 8);
486 * Fills the supplied array with the blue component of each color in the
489 * @param b an array that is at least as large as {@link #getMapSize()}.
490 * @throws NullPointerException if <code>b</code> is <code>null</code>.
491 * @throws ArrayIndexOutOfBoundsException if <code>b</code> has less
492 * than {@link #getMapSize()} elements.
494 public final void getBlues(byte[] b
)
497 for (i
= 0; i
< map_size
; i
++)
498 b
[i
] = (byte) (0x000000FF & rgb
[i
]);
502 * Fills the supplied array with the alpha component of each color in the
503 * lookup table. If the model has a transparent pixel specified, the alpha
504 * for that pixel will be 0.
506 * @param a an array that is at least as large as {@link #getMapSize()}.
507 * @throws NullPointerException if <code>a</code> is <code>null</code>.
508 * @throws ArrayIndexOutOfBoundsException if <code>a</code> has less
509 * than {@link #getMapSize()} elements.
511 public final void getAlphas(byte[] a
)
514 for (i
= 0; i
< map_size
; i
++)
518 a
[i
] = (byte) ((0xFF000000 & rgb
[i
]) >> 24);
522 * Returns the red component of the color in the lookup table for the
525 * @param pixel the pixel lookup value.
527 * @return The red component of the color in the lookup table.
528 * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
530 public final int getRed(int pixel
)
532 if (pixel
< map_size
)
533 return (0x00FF0000 & rgb
[pixel
]) >> 16;
539 * Returns the green component of the color in the lookup table for the
542 * @param pixel the pixel lookup value.
544 * @return The green component of the color in the lookup table.
545 * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
547 public final int getGreen(int pixel
)
549 if (pixel
< map_size
)
550 return (0x0000FF00 & rgb
[pixel
]) >> 8;
556 * Returns the blue component of the color in the lookup table for the
559 * @param pixel the pixel lookup value.
561 * @return The blue component of the color in the lookup table.
562 * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
564 public final int getBlue(int pixel
)
566 if (pixel
< map_size
)
567 return 0x000000FF & rgb
[pixel
];
573 * Returns the alpha component of the color in the lookup table for the
574 * given pixel value. If no alpha channel was specified when the color model
575 * was created, then 255 is returned for all pixels except the transparent
576 * pixel (if one is defined - see {@link #getTransparentPixel()}) which
577 * returns an alpha of 0.
579 * @param pixel the pixel lookup value.
581 * @return The alpha component of the color in the lookup table (in the
583 * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
585 public final int getAlpha(int pixel
)
587 if (opaque
&& pixel
!= trans
)
589 if ((pixel
== trans
&& trans
!= -1) || pixel
>= map_size
)
592 return (0xFF000000 & rgb
[pixel
]) >> 24;
596 * Get the RGB color value of the given pixel using the default
599 * @param pixel the pixel lookup value.
600 * @return The RGB color value.
601 * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
603 public final int getRGB(int pixel
)
605 if (pixel
>= 0 && pixel
< map_size
)
612 * Get the RGB color values of all pixels in the map using the default
615 * @param rgb The destination array.
617 public final void getRGBs(int[] rgb
)
619 System
.arraycopy(this.rgb
, 0, rgb
, 0, map_size
);
623 * Return <code>true</code> if the lookup table contains valid data for
624 * <code>pixel</code>, and <code>false</code> otherwise.
626 * @param pixel the pixel value used to index the color lookup table.
627 * @return <code>true</code> if <code>pixel</code> is valid,
628 * <code>false</code> otherwise.
630 public boolean isValid(int pixel
)
633 return validBits
.testBit(pixel
);
638 * Return <code>true</code> if all pixels are valid, <code>false</code>
641 * @return <code>true</code> if all pixels are valid, <code>false</code>
644 public boolean isValid()
646 // Generate a bigint with 1's for every pixel
647 BigInteger allbits
= new BigInteger("0");
648 allbits
= allbits
.setBit(map_size
);
649 allbits
= allbits
.subtract(new BigInteger("1"));
650 return allbits
.equals(validBits
);
654 * Returns a binary value ({@link BigInteger}) where each bit represents an
655 * entry in the color lookup table. If the bit is on, the entry is valid.
657 * @return The binary value.
659 public BigInteger
getValidPixels()
665 * Construct a {@link BufferedImage} with rgb pixel values from a
668 * Constructs a new BufferedImage in which each pixel is an RGBA int from
669 * a Raster with index-valued pixels. If this model has no alpha component
670 * or transparent pixel, the type of the new BufferedImage is TYPE_INT_RGB.
671 * Otherwise the type is TYPE_INT_ARGB. If forceARGB is true, the type is
672 * forced to be TYPE_INT_ARGB no matter what.
674 * @param raster The source of pixel values.
675 * @param forceARGB True if type must be TYPE_INT_ARGB.
676 * @return New BufferedImage with RBGA int pixel values.
678 public BufferedImage
convertToIntDiscrete(Raster raster
, boolean forceARGB
)
680 int type
= forceARGB ? BufferedImage
.TYPE_INT_ARGB
681 : ((opaque
&& trans
== -1) ? BufferedImage
.TYPE_INT_RGB
:
682 BufferedImage
.TYPE_INT_ARGB
);
684 // FIXME: assuming that raster has only 1 band since pixels are supposed
685 // to be int indexes.
686 // FIXME: it would likely be more efficient to fetch a complete array,
687 // but it would take much more memory.
688 // FIXME: I'm not sure if transparent pixels or alpha values need special
690 BufferedImage im
= new BufferedImage(raster
.width
, raster
.height
, type
);
691 for (int x
= raster
.minX
; x
< raster
.width
+ raster
.minX
; x
++)
692 for (int y
= raster
.minY
; y
< raster
.height
+ raster
.minY
; y
++)
693 im
.setRGB(x
, y
, rgb
[raster
.getSample(x
, y
, 0)]);