001/* IndexColorModel.java -- Java class for interpreting Pixel objects
002   Copyright (C) 1999, 2005 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package java.awt.image;
039
040import gnu.java.awt.Buffers;
041
042import java.awt.color.ColorSpace;
043import 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 */
068public 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}