001    /* IndexColorModel.java -- Java class for interpreting Pixel objects
002       Copyright (C) 1999, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package java.awt.image;
039    
040    import gnu.java.awt.Buffers;
041    
042    import java.awt.color.ColorSpace;
043    import java.math.BigInteger;
044    
045    /**
046     * Color model similar to pseudo visual in X11.
047     * <br><br>
048     * This color model maps linear pixel values to actual RGB and alpha colors.
049     * Thus, pixel values are indexes into the color map.  Each color component is
050     * an 8-bit unsigned value.
051     * <br><br>
052     * The <code>IndexColorModel</code> supports a map of valid pixels, allowing
053     * the representation of holes in the the color map.  The valid map is
054     * represented as a {@link BigInteger} where each bit indicates the validity
055     * of the map entry with the same index.
056     * <br><br>
057     * Colors can have alpha components for transparency support.  If alpha
058     * component values aren't given, color values are opaque.  The model also
059     * supports a reserved pixel value to represent completely transparent colors,
060     * no matter what the actual color component values are.
061     * <br><br>
062     * <code>IndexColorModel</code> supports anywhere from 1 to 16 bit index
063     * values.  The allowed transfer types are {@link DataBuffer#TYPE_BYTE} and
064     * {@link DataBuffer#TYPE_USHORT}.
065     *
066     * @author C. Brian Jones (cbj@gnu.org)
067     */
068    public class IndexColorModel extends ColorModel
069    {
070      private int map_size;
071      private boolean opaque;  // no alpha, but doesn't account for trans
072      private int trans = -1;
073      private int[] rgb;
074      private BigInteger validBits = BigInteger.ZERO;
075    
076      /**
077       * Creates a new indexed color model for <code>size</code> color elements
078       * with no alpha component.  Each array must contain at least
079       * <code>size</code> elements.  For each array, the i-th color is described
080       * by reds[i], greens[i] and blues[i].
081       *
082       * @param bits the number of bits needed to represent <code>size</code>
083       *             colors.
084       * @param size the number of colors in the color map.
085       * @param reds the red component of all colors.
086       * @param greens the green component of all colors.
087       * @param blues the blue component of all colors.
088       *
089       * @throws IllegalArgumentException if <code>bits</code> &lt; 1 or
090       *         <code>bits</code> &gt; 16.
091       * @throws NullPointerException if any of the arrays is <code>null</code>.
092       * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater
093       *         than the length of the component arrays.
094       */
095      public IndexColorModel(int bits, int size, byte[] reds, byte[] greens,
096                             byte[] blues)
097      {
098        this(bits, size, reds, greens, blues, (byte[]) null);
099      }
100    
101      /**
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.
106       *
107       * @param bits the number of bits needed to represent <code>size</code>
108       *             colors
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).
115       *
116       * @throws IllegalArgumentException if <code>bits</code> &lt; 1 or
117       *         <code>bits</code> &gt; 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.
121       */
122      public IndexColorModel(int bits, int size, byte[] reds, byte[] greens,
123                             byte[] blues, int trans)
124      {
125        super(bits, nArray(8, (0 <= trans && trans < size) ? 4 : 3),
126            ColorSpace.getInstance(ColorSpace.CS_sRGB),
127            (0 <= trans && trans < size),  // hasAlpha
128            false, OPAQUE,
129            Buffers.smallestAppropriateTransferType(bits));
130        if (bits < 1)
131          throw new IllegalArgumentException("bits < 1");
132        if (bits > 16)
133          throw new IllegalArgumentException("bits > 16");
134        if (size < 1)
135          throw new IllegalArgumentException("size < 1");
136        map_size = size;
137        rgb = createColorMap(bits, size);
138        for (int i = 0; i < size; i++)
139          {
140            rgb[i] = (0xff000000
141                      | ((reds[i] & 0xff) << 16)
142                      | ((greens[i] & 0xff) << 8)
143                      | (blues[i] & 0xff));
144          }
145    
146        setTransparentPixel(trans);
147    
148        // Generate a bigint with 1's for every pixel
149        validBits = validBits.setBit(size).subtract(BigInteger.ONE);
150      }
151    
152      /**
153       * Creates a new indexed color model for <code>size</code> color elements
154       * including alpha.  Each array must contain at least <code>size</code>
155       * elements.  For each array, the i-th color is described
156       * by reds[i], greens[i], blues[i] and alphas[i].
157       *
158       * @param bits the number of bits needed to represent <code>size</code>
159       *             colors.
160       * @param size the number of colors in the color map.
161       * @param reds the red component of all colors.
162       * @param greens the green component of all colors.
163       * @param blues the blue component of all colors.
164       * @param alphas the alpha component of all colors (<code>null</code>
165       *               permitted).
166       *
167       * @throws IllegalArgumentException if <code>bits</code> &lt; 1 or
168       *           <code>bits</code> &gt; 16.
169       * @throws NullPointerException if <code>reds</code>, <code>greens</code> or
170       *         <code>blues</code> is <code>null</code>.
171       * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater
172       *         than the length of the component arrays.
173       */
174      public IndexColorModel(int bits, int size, byte[] reds, byte[] greens,
175                             byte[] blues, byte[] alphas)
176      {
177        super(bits, nArray(8, (alphas == null ? 3 : 4)),
178            ColorSpace.getInstance(ColorSpace.CS_sRGB),
179            (alphas != null), false, TRANSLUCENT,
180            Buffers.smallestAppropriateTransferType(bits));
181        if (bits < 1)
182          throw new IllegalArgumentException("bits < 1");
183        if (bits > 16)
184          throw new IllegalArgumentException("bits > 16");
185        if (size < 1)
186          throw new IllegalArgumentException("size < 1");
187        map_size = size;
188        opaque = (alphas == null);
189    
190        rgb = createColorMap(bits, size);
191        if (alphas == null)
192          {
193            for (int i = 0; i < size; i++)
194              {
195                rgb[i] = (0xff000000
196                          | ((reds[i] & 0xff) << 16)
197                          | ((greens[i] & 0xff) << 8)
198                          | (blues[i] & 0xff));
199              }
200            transparency = OPAQUE;
201          }
202        else
203          {
204            byte alphaZero = (byte) 0x00;
205            byte alphaOne = (byte) 0xFF;
206            for (int i = 0; i < size; i++)
207              {
208                alphaZero = (byte) (alphaZero | alphas[i]);
209                alphaOne = (byte) (alphaOne & alphas[i]);
210                rgb[i] = ((alphas[i] & 0xff) << 24
211                          | ((reds[i] & 0xff) << 16)
212                          | ((greens[i] & 0xff) << 8)
213                          | (blues[i] & 0xff));
214              }
215            if ((alphaZero == (byte) 0x00) || (alphaOne == (byte) 0xFF))
216              transparency = BITMASK;
217            else
218              transparency = TRANSLUCENT;
219          }
220    
221        // Generate a bigint with 1's for every pixel
222        validBits = validBits.setBit(size).subtract(BigInteger.ONE);
223      }
224    
225      /**
226       * Creates a new indexed color model using the color components in
227       * <code>cmap</code>. If <code>hasAlpha</code> is <code>true</code> then
228       * <code>cmap</code> contains an alpha component after each of the red, green
229       * and blue components.
230       *
231       * @param bits the number of bits needed to represent <code>size</code>
232       *             colors
233       * @param size the number of colors in the color map
234       * @param cmap packed color components
235       * @param start the offset of the first color component in <code>cmap</code>
236       * @param hasAlpha <code>cmap</code> has alpha values
237       * @throws IllegalArgumentException if bits &lt; 1, bits &gt; 16, or size
238       *         &lt; 1.
239       * @throws NullPointerException if <code>cmap</code> is <code>null</code>.
240       */
241      public IndexColorModel(int bits, int size, byte[] cmap, int start,
242                             boolean hasAlpha)
243      {
244        this(bits, size, cmap, start, hasAlpha, -1);
245      }
246    
247      /**
248       * Construct an IndexColorModel from an array of red, green, blue, and
249       * optional alpha components. The component values are interleaved as RGB(A).
250       *
251       * @param bits the number of bits needed to represent <code>size</code>
252       *             colors
253       * @param size the number of colors in the color map
254       * @param cmap interleaved color components
255       * @param start the offset of the first color component in <code>cmap</code>
256       * @param hasAlpha <code>cmap</code> has alpha values
257       * @param trans the index of the transparent color
258       * @throws IllegalArgumentException if bits &lt; 1, bits &gt; 16, or size
259       *         &lt; 1.
260       * @throws NullPointerException if <code>cmap</code> is <code>null</code>.
261       */
262      public IndexColorModel(int bits, int size, byte[] cmap, int start,
263                             boolean hasAlpha, int trans)
264      {
265        super(bits, nArray(8, hasAlpha || (0 <= trans && trans < size) ? 4 : 3),
266            ColorSpace.getInstance(ColorSpace.CS_sRGB),
267            hasAlpha || (0 <= trans && trans < size), false, OPAQUE,
268            Buffers.smallestAppropriateTransferType(bits));
269        if (bits < 1)
270          throw new IllegalArgumentException("bits < 1");
271        if (bits > 16)
272          throw new IllegalArgumentException("bits > 16");
273        if (size < 1)
274          throw new IllegalArgumentException("size < 1");
275        map_size = size;
276        opaque = !hasAlpha;
277    
278        rgb = createColorMap(bits, size);
279        if (hasAlpha)
280        {
281          int alpha;
282          int alphaZero = 0x00;  // use to detect all zeros
283          int alphaOne = 0xff;   // use to detect all ones
284          for (int i = 0; i < size; i++) {
285            alpha = cmap[4 * i + 3 + start] & 0xff;
286            alphaZero = alphaZero | alpha;
287            alphaOne = alphaOne & alpha;
288            rgb[i] =
289              ( alpha << 24
290               // red
291               | ((cmap[4 * i + start] & 0xff) << 16)
292               // green
293               | ((cmap[4 * i + 1 + start] & 0xff) << 8)
294               // blue
295               | (cmap[4 * i + 2 + start] & 0xff));
296          }
297          if (alphaZero == 0)
298            transparency = BITMASK;
299          else if (alphaOne == 255)
300            transparency = (trans != -1 ? BITMASK : OPAQUE);
301          else
302            transparency = TRANSLUCENT;
303        }
304        else
305        {
306          for (int i = 0; i < size; i++)
307            rgb[i] = (0xff000000
308                      // red
309                      | ((cmap[3 * i + start] & 0xff) << 16)
310                      // green
311                      | ((cmap[3 * i + 1 + start] & 0xff) << 8)
312                      // blue
313                      | (cmap[3 * i + 2 + start] & 0xff));
314          if (trans != -1)
315            transparency = BITMASK;
316        }
317    
318        setTransparentPixel(trans);
319    
320        // Generate a bigint with 1's for every pixel
321        validBits = validBits.setBit(size).subtract(BigInteger.ONE);
322      }
323    
324      /**
325       * Construct an IndexColorModel from an array of <code>size</code> packed
326       * colors.  Each int element contains 8-bit red, green, blue, and optional
327       * alpha values packed in order.  If hasAlpha is false, then all the colors
328       * are opaque except for the transparent color.
329       *
330       * @param bits the number of bits needed to represent <code>size</code>
331       *             colors
332       * @param size the number of colors in the color map
333       * @param cmap packed color components
334       * @param start the offset of the first color component in <code>cmap</code>
335       * @param hasAlpha <code>cmap</code> has alpha values
336       * @param trans the index of the transparent color
337       * @param transferType {@link DataBuffer#TYPE_BYTE} or
338                {@link DataBuffer#TYPE_USHORT}.
339       * @throws IllegalArgumentException if bits &lt; 1, bits &gt; 16, or size
340       *         &lt; 1.
341       * @throws IllegalArgumentException if <code>transferType</code> is something
342       *         other than {@link DataBuffer#TYPE_BYTE} or
343       *         {@link DataBuffer#TYPE_USHORT}.
344       */
345      public IndexColorModel(int bits, int size, int[] cmap, int start,
346                             boolean hasAlpha, int trans, int transferType)
347      {
348        super(bits,
349              nArray(8, 4), // bits for each channel
350              ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB
351              true, // has alpha
352              false, // not premultiplied
353              TRANSLUCENT, transferType);
354        if (transferType != DataBuffer.TYPE_BYTE
355            && transferType != DataBuffer.TYPE_USHORT)
356          throw new IllegalArgumentException();
357        if (bits > 16)
358          throw new IllegalArgumentException("bits > 16");
359        if (size < 1)
360          throw new IllegalArgumentException("size < 1");
361        map_size = size;
362        opaque = !hasAlpha;
363        rgb = createColorMap(bits, size);
364        if (!hasAlpha)
365          for (int i = 0; i < size; i++)
366            rgb[i] = cmap[i + start] | 0xff000000;
367        else
368          System.arraycopy(cmap, start, rgb, 0, size);
369    
370        setTransparentPixel(trans);
371    
372        // Generate a bigint with 1's for every pixel
373        validBits = validBits.setBit(size).subtract(BigInteger.ONE);
374      }
375    
376      /**
377       * Construct an IndexColorModel using a colormap with holes.
378       * <br><br>
379       * The IndexColorModel is built from the array of ints defining the
380       * colormap.  Each element contains red, green, blue, and alpha
381       * components.    The ColorSpace is sRGB.  The transparency value is
382       * automatically determined.
383       * <br><br>
384       * This constructor permits indicating which colormap entries are valid,
385       * using the validBits argument.  Each entry in cmap is valid if the
386       * corresponding bit in validBits is set.
387       *
388       * @param bits the number of bits needed to represent <code>size</code>
389       *             colors.
390       * @param size the number of colors in the color map.
391       * @param cmap packed color components.
392       * @param start the offset of the first color component in <code>cmap</code>.
393       * @param transferType {@link DataBuffer#TYPE_BYTE} or
394       *                     {@link DataBuffer#TYPE_USHORT}.
395       * @param validBits a map of the valid entries in <code>cmap</code>.
396       * @throws IllegalArgumentException if bits &lt; 1, bits &gt; 16, or size
397       *         &lt; 1.
398       * @throws IllegalArgumentException if transferType is something other than
399       *         {@link DataBuffer#TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT}.
400       */
401      public IndexColorModel(int bits, int size, int[] cmap, int start,
402                             int transferType, BigInteger validBits)
403      {
404        super(bits, // total bits, sRGB, four channels
405              nArray(8, 4), // bits for each channel
406              ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB
407              true, // has alpha
408              false, // not premultiplied
409              TRANSLUCENT, transferType);
410        if (transferType != DataBuffer.TYPE_BYTE
411            && transferType != DataBuffer.TYPE_USHORT)
412          throw new IllegalArgumentException();
413        if (bits > 16)
414          throw new IllegalArgumentException("bits > 16");
415        if (size < 1)
416          throw new IllegalArgumentException("size < 1");
417        map_size = size;
418        opaque = false;
419        this.trans = -1;
420        this.validBits = validBits;
421    
422        rgb = createColorMap(bits, size);
423        if (!hasAlpha)
424          for (int i = 0; i < size; i++)
425            rgb[i] = cmap[i + start] | 0xff000000;
426        else
427          System.arraycopy(cmap, start, rgb, 0, size);
428      }
429    
430      /**
431       * Returns the size of the color lookup table.
432       *
433       * @return The size of the color lookup table.
434       */
435      public final int getMapSize()
436      {
437        return map_size;
438      }
439    
440      /**
441       * Get the index of the transparent color in this color model.
442       *
443       * @return The index of the color that is considered transparent, or -1 if
444       *         there is no transparent color.
445       */
446      public final int getTransparentPixel()
447      {
448        return trans;
449      }
450    
451      /**
452       * Fills the supplied array with the red component of each color in the
453       * lookup table.
454       *
455       * @param r an array that is at least as large as {@link #getMapSize()}.
456       * @throws NullPointerException if <code>r</code> is <code>null</code>.
457       * @throws ArrayIndexOutOfBoundsException if <code>r</code> has less
458       *         than {@link #getMapSize()} elements.
459       */
460      public final void getReds(byte[] r)
461      {
462        int i;
463        for (i = 0; i < map_size; i++)
464          r[i] = (byte) ((0x00FF0000  & rgb[i]) >> 16);
465      }
466    
467      /**
468       * Fills the supplied array with the green component of each color in the
469       * lookup table.
470       *
471       * @param g an array that is at least as large as {@link #getMapSize()}.
472       * @throws NullPointerException if <code>g</code> is <code>null</code>.
473       * @throws ArrayIndexOutOfBoundsException if <code>g</code> has less
474       *         than {@link #getMapSize()} elements.
475       */
476      public final void getGreens(byte[] g)
477      {
478        int i;
479        for (i = 0; i < map_size; i++)
480          g[i] = (byte) ((0x0000FF00  & rgb[i]) >> 8);
481      }
482    
483      /**
484       * Fills the supplied array with the blue component of each color in the
485       * lookup table.
486       *
487       * @param b an array that is at least as large as {@link #getMapSize()}.
488       * @throws NullPointerException if <code>b</code> is <code>null</code>.
489       * @throws ArrayIndexOutOfBoundsException if <code>b</code> has less
490       *         than {@link #getMapSize()} elements.
491       */
492      public final void getBlues(byte[] b)
493      {
494        int i;
495        for (i = 0; i < map_size; i++)
496          b[i] = (byte) (0x000000FF & rgb[i]);
497      }
498    
499      /**
500       * Fills the supplied array with the alpha component of each color in the
501       * lookup table.  If the model has a transparent pixel specified, the alpha
502       * for that pixel will be 0.
503       *
504       * @param a an array that is at least as large as {@link #getMapSize()}.
505       * @throws NullPointerException if <code>a</code> is <code>null</code>.
506       * @throws ArrayIndexOutOfBoundsException if <code>a</code> has less
507       *         than {@link #getMapSize()} elements.
508       */
509      public final void getAlphas(byte[] a)
510      {
511        int i;
512        for (i = 0; i < map_size; i++)
513          if (i == trans)
514            a[i] = (byte) 0;
515          else
516            a[i] = (byte) ((0xFF000000  & rgb[i]) >> 24);
517      }
518    
519      /**
520       * Returns the red component of the color in the lookup table for the
521       * given pixel value.
522       *
523       * @param pixel  the pixel lookup value.
524       *
525       * @return The red component of the color in the lookup table.
526       * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
527       */
528      public final int getRed(int pixel)
529      {
530        if (pixel < map_size)
531          return (0x00FF0000 & rgb[pixel]) >> 16;
532    
533        return 0;
534      }
535    
536      /**
537       * Returns the green component of the color in the lookup table for the
538       * given pixel value.
539       *
540       * @param pixel  the pixel lookup value.
541       *
542       * @return The green component of the color in the lookup table.
543       * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
544       */
545      public final int getGreen(int pixel)
546      {
547        if (pixel < map_size)
548          return (0x0000FF00 & rgb[pixel]) >> 8;
549    
550        return 0;
551      }
552    
553      /**
554       * Returns the blue component of the color in the lookup table for the
555       * given pixel value.
556       *
557       * @param pixel  the pixel lookup value.
558       *
559       * @return The blue component of the color in the lookup table.
560       * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
561       */
562      public final int getBlue(int pixel)
563      {
564        if (pixel < map_size)
565          return 0x000000FF & rgb[pixel];
566    
567        return 0;
568      }
569    
570      /**
571       * Returns the alpha component of the color in the lookup table for the
572       * given pixel value. If no alpha channel was specified when the color model
573       * was created, then 255 is returned for all pixels except the transparent
574       * pixel (if one is defined - see {@link #getTransparentPixel()}) which
575       * returns an alpha of 0.
576       *
577       * @param pixel  the pixel lookup value.
578       *
579       * @return The alpha component of the color in the lookup table (in the
580       *         range 0 to 255).
581       * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
582       */
583      public final int getAlpha(int pixel)
584      {
585        return (rgb[pixel] >> 24) & 0xFF;
586      }
587    
588      /**
589       * Get the RGB color value of the given pixel using the default
590       * RGB color model.
591       *
592       * @param pixel the pixel lookup value.
593       * @return The RGB color value.
594       * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative.
595       */
596      public final int getRGB(int pixel)
597      {
598        if (pixel >= 0 && pixel < map_size)
599          return rgb[pixel];
600    
601        return 0;
602      }
603    
604      /**
605       * Get the RGB color values of all pixels in the map using the default
606       * RGB color model.
607       *
608       * @param rgb The destination array.
609       */
610      public final void getRGBs(int[] rgb)
611      {
612        System.arraycopy(this.rgb, 0, rgb, 0, map_size);
613      }
614    
615      /**
616       * Return <code>true</code> if the lookup table contains valid data for
617       * <code>pixel</code>, and <code>false</code> otherwise.
618       *
619       * @param pixel  the pixel value used to index the color lookup table.
620       * @return <code>true</code> if <code>pixel</code> is valid,
621       *         <code>false</code> otherwise.
622       */
623      public boolean isValid(int pixel)
624      {
625        if (pixel >= 0)
626          return validBits.testBit(pixel);
627        return false;
628      }
629    
630      /**
631       * Return <code>true</code> if all pixels are valid, <code>false</code>
632       * otherwise.
633       *
634       * @return <code>true</code> if all pixels are valid, <code>false</code>
635       * otherwise.
636       */
637      public boolean isValid()
638      {
639        // Generate a bigint with 1's for every pixel
640        BigInteger allbits = new BigInteger("0");
641        allbits = allbits.setBit(map_size);
642        allbits = allbits.subtract(new BigInteger("1"));
643        return allbits.equals(validBits);
644      }
645    
646      /**
647       * Returns a binary value ({@link BigInteger}) where each bit represents an
648       * entry in the color lookup table.  If the bit is on, the entry is valid.
649       *
650       * @return The binary value.
651       */
652      public BigInteger getValidPixels()
653      {
654        return validBits;
655      }
656    
657      /**
658       * Construct a {@link BufferedImage} with rgb pixel values from a
659       * {@link Raster}.
660       *
661       * Constructs a new BufferedImage in which each pixel is an RGBA int from
662       * a Raster with index-valued pixels.  If this model has no alpha component
663       * or transparent pixel, the type of the new BufferedImage is TYPE_INT_RGB.
664       * Otherwise the type is TYPE_INT_ARGB.  If forceARGB is true, the type is
665       * forced to be TYPE_INT_ARGB no matter what.
666       *
667       * @param raster The source of pixel values.
668       * @param forceARGB True if type must be TYPE_INT_ARGB.
669       * @return New BufferedImage with RBGA int pixel values.
670       */
671      public BufferedImage convertToIntDiscrete(Raster raster, boolean forceARGB)
672      {
673        int type = forceARGB ? BufferedImage.TYPE_INT_ARGB
674          : ((opaque && trans == -1) ? BufferedImage.TYPE_INT_RGB :
675             BufferedImage.TYPE_INT_ARGB);
676    
677        // FIXME: assuming that raster has only 1 band since pixels are supposed
678        // to be int indexes.
679        // FIXME: it would likely be more efficient to fetch a complete array,
680        // but it would take much more memory.
681        // FIXME: I'm not sure if transparent pixels or alpha values need special
682        // handling here.
683        BufferedImage im = new BufferedImage(raster.width, raster.height, type);
684        for (int x = raster.minX; x < raster.width + raster.minX; x++)
685          for (int y = raster.minY; y < raster.height + raster.minY; y++)
686            im.setRGB(x, y, rgb[raster.getSample(x, y, 0)]);
687    
688        return im;
689      }
690    
691      /**
692       * Creates a {@link SampleModel} that is compatible to this color model.
693       * This will be a {@link MultiPixelPackedSampleModel} for bits/pixel of
694       * 1, 2 or 4, or a {@link ComponentColorModel} for the other cases.
695       *
696       * @param w the width of the sample model to create
697       * @param h the height of the sample model to create
698       *
699       * @return a compatible sample model
700       */
701      public SampleModel createCompatibleSampleModel(int w, int h)
702      {
703        SampleModel sm;
704        if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4)
705          sm = new MultiPixelPackedSampleModel(transferType, w, h, pixel_bits);
706        else
707          sm = new ComponentSampleModel(transferType, w, h, 1, w, new int[]{0});
708        return sm;
709      }
710    
711      /**
712       * Sets the transparent pixel. This is called by the various constructors.
713       *
714       * @param t the transparent pixel
715       */
716      private void setTransparentPixel(int t)
717      {
718        if (t >= 0 && t < map_size)
719          {
720            rgb[t] &= 0xffffff; // Make the value transparent.
721            trans = t;
722            if (transparency == OPAQUE)
723              {
724                transparency = BITMASK;
725                hasAlpha = true;
726              }
727          }
728      }
729    
730      private int[] createColorMap(int bits, int size)
731      {
732        // According to a Mauve test, the RI allocates at least 256 entries here.
733        int realSize = Math.max(256, Math.max(1 << bits, size));
734        return new int[realSize];
735      }
736    }