001/* ColorModel.java --
002   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006  Free Software Foundation
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
038
039package java.awt.image;
040
041import gnu.java.awt.Buffers;
042
043import java.awt.Point;
044import java.awt.Transparency;
045import java.awt.color.ColorSpace;
046import java.util.Arrays;
047
048/**
049 * A color model operates with colors in several formats:
050 *
051 * <ul>
052 * <li>normalized: component samples are in range [0.0, 1.0].</li>
053 *
054 * <li>color model pixel value: all the color component samples for a
055 * sigle pixel packed/encoded in a way natural for the color
056 * model.</li>
057 *
058 * <li>color model pixel int value: only makes sense if the natural
059 * encoding of a single pixel can fit in a single int value.</li>
060 *
061 * <li>array of transferType containing a single pixel: the pixel is
062 * encoded in the natural way of the color model, taking up as many
063 * array elements as needed.</li>
064 *
065 * <li>sRGB pixel int value: a pixel in sRGB color space, encoded in
066 * default 0xAARRGGBB format, assumed not alpha premultiplied.</li>
067 *
068 * <li>single [0, 255] scaled int samples from default sRGB color
069 * space. These are always assumed to be alpha non-premultiplied.</li>
070 *
071 * <li>arrays of unnormalized component samples of single pixel: these
072 * samples are scaled and multiplied according to the color model, but
073 * is otherwise not packed or encoded. Each element of the array is one
074 * separate component sample. The color model only operate on the
075 * components from one pixel at a time, but using offsets, allows
076 * manipulation of arrays that contain the components of more than one
077 * pixel.</li>
078 *
079 * </ul>
080 *
081 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
082 * @author C. Brian Jones (cbj@gnu.org)
083 */
084public abstract class ColorModel implements Transparency
085{
086  protected int pixel_bits;
087  protected int transferType;
088
089  int[] bits;
090  ColorSpace cspace;
091  int transparency;
092  boolean hasAlpha;
093  boolean isAlphaPremultiplied;
094
095  /**
096   * The standard color model for the common sRGB.
097   */
098  private static final ColorModel S_RGB_MODEL = new SRGBColorModel();
099
100  static int[] nArray(int value, int times)
101  {
102    int[] array = new int[times];
103    java.util.Arrays.fill(array, value);
104    return array;
105  }
106
107  static byte[] nArray(byte value, int times)
108  {
109    byte[] array = new byte[times];
110    java.util.Arrays.fill(array, value);
111    return array;
112  }
113
114  /**
115   * Constructs the default color model.  The default color model
116   * can be obtained by calling <code>getRGBdefault</code> of this
117   * class.
118   * @param bits the number of bits wide used for bit size of pixel values
119   */
120  public ColorModel(int bits)
121  {
122    this(bits * 4, // total bits, sRGB, four channels
123         nArray(bits, 4), // bits for each channel
124         ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB
125         true, // has alpha
126         false, // not premultiplied
127         TRANSLUCENT,
128         Buffers.smallestAppropriateTransferType(bits * 4));
129  }
130
131  /**
132   * Constructs a ColorModel that translates pixel values to
133   * color/alpha components.
134   *
135   * @exception IllegalArgumentException If the length of the bit array is less
136   * than the number of color or alpha components in this ColorModel, or if the
137   * transparency is not a valid value, or if the sum of the number of bits in
138   * bits is less than 1 or if any of the elements in bits is less than 0.
139   */
140  protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace,
141                       boolean hasAlpha, boolean isAlphaPremultiplied,
142                       int transparency, int transferType)
143  {
144    int bits_sum = 0;
145    for (int i = 0; i < bits.length; i++)
146      {
147        if (bits [i] < 0)
148          throw new IllegalArgumentException ();
149
150        bits_sum |= bits [i];
151      }
152
153    if ((bits.length < cspace.getNumComponents())
154        || (bits_sum < 1))
155      throw new IllegalArgumentException ();
156
157    this.pixel_bits = pixel_bits;
158    this.bits = bits;
159    this.cspace = cspace;
160    this.hasAlpha = hasAlpha;
161    this.isAlphaPremultiplied = isAlphaPremultiplied;
162    this.transparency = transparency;
163    this.transferType = transferType;
164  }
165
166  public void finalize()
167  {
168    // Do nothing here.
169  }
170
171  /**
172   * Returns the default color model which in Sun's case is an instance
173   * of <code>DirectColorModel</code>.
174   */
175  public static ColorModel getRGBdefault()
176  {
177    return S_RGB_MODEL;
178  }
179
180  public final boolean hasAlpha()
181  {
182    return hasAlpha;
183  }
184
185  public final boolean isAlphaPremultiplied()
186  {
187    return isAlphaPremultiplied;
188  }
189
190  /**
191   * Get get number of bits wide used for the bit size of pixel values
192   */
193  public int getPixelSize()
194  {
195    return pixel_bits;
196  }
197
198  public int getComponentSize(int componentIdx)
199  {
200    return bits[componentIdx];
201  }
202
203  public int[] getComponentSize()
204  {
205    return bits;
206  }
207
208  public int getTransparency()
209  {
210    return transparency;
211  }
212
213  public int getNumComponents()
214  {
215    return getNumColorComponents() + (hasAlpha ? 1 : 0);
216  }
217
218  public int getNumColorComponents()
219  {
220    return cspace.getNumComponents();
221  }
222
223  /**
224   * Converts pixel value to sRGB and extract red int sample scaled
225   * to range [0, 255].
226   *
227   * @param pixel pixel value that will be interpreted according to
228   * the color model, (assumed alpha premultiplied if color model says
229   * so.)
230   *
231   * @return red sample scaled to range [0, 255], from default color
232   * space sRGB, alpha non-premultiplied.
233   */
234  public abstract int getRed(int pixel);
235
236  /**
237   * Converts pixel value to sRGB and extract green int sample
238   * scaled to range [0, 255].
239   *
240   * @see #getRed(int)
241   */
242  public abstract int getGreen(int pixel);
243
244  /**
245   * Converts pixel value to sRGB and extract blue int sample
246   * scaled to range [0, 255].
247   *
248   * @see #getRed(int)
249   */
250  public abstract int getBlue(int pixel);
251
252  /**
253   * Extract alpha int sample from pixel value, scaled to [0, 255].
254   *
255   * @param pixel pixel value that will be interpreted according to
256   * the color model.
257   *
258   * @return alpha sample, scaled to range [0, 255].
259   */
260  public abstract int getAlpha(int pixel);
261
262  /**
263   * Converts a pixel int value of the color space of the color
264   * model to a sRGB pixel int value.
265   *
266   * This method is typically overriden in subclasses to provide a
267   * more efficient implementation.
268   *
269   * @param pixel pixel value that will be interpreted according to
270   * the color model.
271   *
272   * @return a pixel in sRGB color space, encoded in default
273   * 0xAARRGGBB format.  */
274  public int getRGB(int pixel)
275  {
276    return
277      ((getAlpha(pixel) & 0xff) << 24) |
278      ((  getRed(pixel) & 0xff) << 16) |
279      ((getGreen(pixel) & 0xff) <<  8) |
280      (( getBlue(pixel) & 0xff) <<  0);
281  }
282
283
284  /**
285   * In this color model we know that the whole pixel value will
286   * always be contained within the first element of the pixel
287   * array.
288   */
289  final int getPixelFromArray(Object inData) {
290    DataBuffer data =
291      Buffers.createBufferFromData(transferType, inData, 1);
292    Object da = Buffers.getData(data);
293
294    return data.getElem(0);
295  }
296
297  /**
298   * Converts pixel in the given array to sRGB and extract blue int
299   * sample scaled to range [0-255].
300   *
301   * This method is typically overriden in subclasses to provide a
302   * more efficient implementation.
303   *
304   * @param inData array of transferType containing a single pixel.  The
305   * pixel should be encoded in the natural way of the color model.
306   */
307  public int getRed(Object inData)
308  {
309    return getRed(getPixelFromArray(inData));
310  }
311
312  /**
313   * @see #getRed(Object)
314   */
315  public int getGreen(Object inData)
316  {
317    return getGreen(getPixelFromArray(inData));
318  }
319
320  /**
321   * @see #getRed(Object)
322   */
323  public int getBlue(Object inData) {
324    return getBlue(getPixelFromArray(inData));
325  }
326
327  /**
328   * @see #getRed(Object)
329   */
330  public int getAlpha(Object inData) {
331    return getAlpha(getPixelFromArray(inData));
332  }
333
334  /**
335   * Converts a pixel in the given array of the color space of the
336   * color model to an sRGB pixel int value.
337   *
338   * <p>This method performs the inverse function of
339   * <code>getDataElements(int rgb, Object pixel)</code>.
340   * I.e. <code>(rgb == cm.getRGB(cm.getDataElements(rgb,
341   * null)))</code>.
342   *
343   * @param inData array of transferType containing a single pixel. The
344   * pixel should be encoded in the natural way of the color model.
345   *
346   * @return a pixel in sRGB color space, encoded in default
347   * 0xAARRGGBB format.
348   *
349   * @see #getDataElements(int, Object)
350   */
351  public int getRGB(Object inData)
352  {
353    return
354      ((getAlpha(inData) & 0xff) << 24) |
355      ((  getRed(inData) & 0xff) << 16) |
356      ((getGreen(inData) & 0xff) <<  8) |
357      (( getBlue(inData) & 0xff) <<  0);
358  }
359
360  /**
361   * Converts an sRGB pixel int value to an array containing a
362   * single pixel of the color space of the color model.
363   *
364   * <p>This method performs the inverse function of
365   * <code>getRGB(Object inData)</code>.
366   *
367   * Outline of conversion process:
368   *
369   * <ol>
370   *
371   * <li>Convert rgb to normalized [0.0, 1.0] sRGB values.</li>
372   *
373   * <li>Convert to color space components using fromRGB in
374   * ColorSpace.</li>
375   *
376   * <li>If color model has alpha and should be premultiplied,
377   * multiply color space components with alpha value</li>
378   *
379   * <li>Scale the components to the correct number of bits.</li>
380   *
381   * <li>Arrange the components in the output array</li>
382   *
383   * </ol>
384   *
385   * @param rgb The color to be converted to dataElements.  A pixel
386   * in sRGB color space, encoded in default 0xAARRGGBB format,
387   * assumed not alpha premultiplied.
388   *
389   * @param pixel to avoid needless creation of arrays, an array to
390   * use to return the pixel can be given. If null, a suitable array
391   * will be created.
392   *
393   * @return An array of transferType values representing the color,
394   * in the color model format. The color model defines whether the
395   *
396   * @see #getRGB(Object)
397   */
398  public Object getDataElements(int rgb, Object pixel)
399  {
400    // subclasses has to implement this method.
401    throw new UnsupportedOperationException();
402  }
403
404  /**
405   * Fills an array with the unnormalized component samples from a
406   * pixel value. I.e. decompose the pixel, but not perform any
407   * color conversion.
408   *
409   * This method is typically overriden in subclasses to provide a
410   * more efficient implementation.
411   *
412   * @param pixel pixel value encoded according to the color model.
413   *
414   * @return arrays of unnormalized component samples of single
415   * pixel.  The scale and multiplication state of the samples are
416   * according to the color model. Each component sample is stored
417   * as a separate element in the array.
418   */
419  public int[] getComponents(int pixel, int[] components, int offset)
420  {
421    // subclasses has to implement this method.
422    throw new UnsupportedOperationException();
423  }
424
425  /**
426   * Fills an array with the unnormalized component samples from an
427   * array of transferType containing a single pixel. I.e. decompose
428   * the pixel, but not perform any color conversion.
429   *
430   * This method is typically overriden in subclasses to provide a
431   * more efficient implementation.
432   *
433   * @param pixel an array of transferType containing a single pixel.  The
434   * pixel should be encoded in the natural way of the color model.  If
435   * this argument is not an array, as expected, a {@link ClassCastException}
436   * will be thrown.
437   * @param components an array that will be filled with the color component
438   * of the pixel.  If this is null, a new array will be allocated
439   * @param offset index into the components array at which the result
440   * will be stored
441   *
442   * @return arrays of unnormalized component samples of single
443   * pixel.  The scale and multiplication state of the samples are
444   * according to the color model. Each component sample is stored
445   * as a separate element in the array.
446   */
447  public int[] getComponents(Object pixel, int[] components, int offset)
448  {
449    // subclasses has to implement this method.
450    throw new UnsupportedOperationException();
451  }
452
453  /**
454   * Convert normalized components to unnormalized components.
455   */
456  public int[] getUnnormalizedComponents(float[] normComponents,
457                                         int normOffset,
458                                         int[] components,
459                                         int offset)
460  {
461    int numComponents = getNumComponents();
462    if (components == null)
463    {
464      components = new int[offset + numComponents];
465    }
466
467    for (int i=0; i<numComponents; i++)
468    {
469      float in = normComponents[normOffset++];
470      int out = (int) (in * ((1<<getComponentSize(i)) - 1));
471      components[offset++] = out;
472    }
473    return components;
474  }
475
476  /**
477   * Convert unnormalized components to normalized components.
478   */
479  public float[] getNormalizedComponents(int[] components,
480                                         int offset,
481                                         float[] normComponents,
482                                         int normOffset)
483  {
484    int numComponents = getNumComponents();
485    if (normComponents == null)
486    {
487      normComponents = new float[normOffset + numComponents];
488    }
489
490    for (int i=0; i<numComponents; i++)
491    {
492      float in = components[offset++];
493      float out = in / ((1<<getComponentSize(i)) - 1);
494      normComponents[normOffset++] = out;
495    }
496    return normComponents;
497  }
498
499  /**
500   * Convert unnormalized components to normalized components.
501   *
502   * @since 1.4
503   */
504  public float[] getNormalizedComponents (Object pixel,
505                                          float[] normComponents,
506                                          int normOffset)
507  {
508    int[] components = getComponents(pixel, null, 0);
509    return getNormalizedComponents(components, 0, normComponents, normOffset);
510  }
511
512  /**
513   * Converts the unnormalized component samples from an array to a
514   * pixel value. I.e. composes the pixel from component samples, but
515   * does not perform any color conversion or scaling of the samples.
516   *
517   * This method performs the inverse function of
518   * <code>getComponents(int pixel, int[] components,
519   *                           int offset)</code>. I.e.
520   *
521   * <code>(pixel == cm.getDataElement(cm.getComponents(pixel, null,
522   * 0), 0))</code>.
523   *
524   * This method is overriden in subclasses since this abstract class throws
525   * UnsupportedOperationException().
526   *
527   * @param components Array of unnormalized component samples of single
528   * pixel.  The scale and multiplication state of the samples are according
529   * to the color model. Each component sample is stored as a separate element
530   * in the array.
531   * @param offset Position of the first value of the pixel in components.
532   *
533   * @return pixel value encoded according to the color model.
534   */
535  public int getDataElement(int[] components, int offset)
536  {
537    // subclasses have to implement this method.
538    throw new UnsupportedOperationException();
539  }
540
541  /**
542   * Converts the normalized component samples from an array to a pixel
543   * value. I.e. composes the pixel from component samples, but does not
544   * perform any color conversion or scaling of the samples.
545   *
546   * This method is typically overriden in subclasses to provide a
547   * more efficient implementation.  The method provided by this abstract
548   * class converts the components to unnormalized form and returns
549   * getDataElement(int[], int).
550   *
551   * @param components Array of normalized component samples of single pixel.
552   * The scale and multiplication state of the samples are according to the
553   * color model. Each component sample is stored as a separate element in the
554   * array.
555   * @param offset Position of the first value of the pixel in components.
556   *
557   * @return pixel value encoded according to the color model.
558   * @since 1.4
559   */
560  public int getDataElement (float[] components, int offset)
561  {
562    return
563      getDataElement(getUnnormalizedComponents(components, offset, null, 0),
564                     0);
565  }
566
567  public Object getDataElements(int[] components, int offset, Object obj)
568  {
569    // subclasses have to implement this method.
570    throw new UnsupportedOperationException();
571  }
572
573  /**
574   * Converts the normalized component samples from an array to an array of
575   * TransferType values. I.e. composes the pixel from component samples, but
576   * does not perform any color conversion or scaling of the samples.
577   *
578   * If obj is null, a new array of TransferType is allocated and returned.
579   * Otherwise the results are stored in obj and obj is returned.  If obj is
580   * not long enough, ArrayIndexOutOfBounds is thrown.  If obj is not an array
581   * of primitives, ClassCastException is thrown.
582   *
583   * This method is typically overriden in subclasses to provide a
584   * more efficient implementation.  The method provided by this abstract
585   * class converts the components to unnormalized form and returns
586   * getDataElement(int[], int, Object).
587   *
588   * @param components Array of normalized component samples of single pixel.
589   * The scale and multiplication state of the samples are according to the
590   * color model. Each component sample is stored as a separate element in the
591   * array.
592   * @param offset Position of the first value of the pixel in components.
593   * @param obj Array of TransferType or null.
594   *
595   * @return pixel value encoded according to the color model.
596   * @throws ArrayIndexOutOfBoundsException
597   * @throws ClassCastException
598   * @since 1.4
599   */
600  public Object getDataElements(float[] components, int offset, Object obj)
601  {
602    return
603      getDataElements(getUnnormalizedComponents(components, offset, null, 0),
604                      0, obj);
605  }
606
607  public boolean equals(Object obj)
608  {
609    if (!(obj instanceof ColorModel)) return false;
610
611    ColorModel o = (ColorModel) obj;
612    return
613      (pixel_bits == o.pixel_bits) &&
614      (transferType == o.transferType) &&
615      (transparency == o.transparency) &&
616      (hasAlpha == o.hasAlpha) &&
617      (isAlphaPremultiplied == o.isAlphaPremultiplied) &&
618      Arrays.equals(bits, o.bits) &&
619      (cspace.equals(o.cspace));
620  }
621
622  public final ColorSpace getColorSpace()
623  {
624    return cspace;
625  }
626
627  public ColorModel coerceData(WritableRaster raster,
628                               boolean isAlphaPremultiplied)
629  {
630    // This method should always be overridden, but is not abstract.
631    throw new UnsupportedOperationException();
632  }
633
634  void coerceDataWorker(WritableRaster raster,
635                        boolean isAlphaPremultiplied)
636  {
637    int w = raster.getWidth();
638    int h = raster.getHeight();
639    int x = raster.getMinX();
640    int y = raster.getMinY();
641    int size = w * h;
642    int numColors = getNumColorComponents();
643    int numComponents = getNumComponents();
644    int alphaScale = (1 << getComponentSize(numColors)) - 1;
645    double[] pixels = raster.getPixels(x, y, w, h, (double[]) null);
646
647    for (int i = 0; i < size; i++)
648      {
649        double alpha = pixels[i * numComponents + numColors] / alphaScale;
650        for (int c = 0; c < numColors; c++)
651          {
652            int offset = i * numComponents + c;
653            if (isAlphaPremultiplied)
654              pixels[offset] = Math.round(pixels[offset] * alpha);
655            else
656              pixels[offset] = Math.round(pixels[offset] / alpha);
657          }
658      }
659
660    raster.setPixels(0, 0, w, h, pixels);
661  }
662
663  /**
664   * Checks if the given raster has a compatible data-layout (SampleModel).
665   * @param raster The Raster to test.
666   * @return true if raster is compatible.
667   */
668  public boolean isCompatibleRaster(Raster raster)
669  {
670    SampleModel sampleModel = raster.getSampleModel();
671    return isCompatibleSampleModel(sampleModel);
672  }
673
674  // Typically overridden
675  public WritableRaster createCompatibleWritableRaster(int w, int h)
676  {
677    return new WritableRaster(createCompatibleSampleModel(w, h),
678                              new Point(0, 0));
679  }
680
681  // Typically overridden
682  public SampleModel createCompatibleSampleModel(int w, int h)
683  {
684    throw new UnsupportedOperationException();
685  }
686
687  // Typically overridden
688  public boolean isCompatibleSampleModel(SampleModel sm)
689  {
690    return sm.getTransferType() == transferType;
691  }
692
693  public final int getTransferType ()
694  {
695    return transferType;
696  }
697
698  /**
699   * Subclasses must override this method if it is possible for the
700   * color model to have an alpha channel.
701   *
702   * @return null, as per JDK 1.3 doc. Subclasses will only return
703   * null if no alpha raster exists.
704   */
705  public WritableRaster getAlphaRaster(WritableRaster raster)
706  {
707    return null;
708
709    /* It is a mystery to me why we couldn't use the following code...
710
711
712       if (!hasAlpha()) return null;
713
714       SampleModel sm = raster.getSampleModel();
715       int[] alphaBand = { sm.getNumBands() - 1 };
716       SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand);
717       DataBuffer buffer = raster.getDataBuffer();
718       Point origin = new Point(0, 0);
719       return Raster.createWritableRaster(alphaModel, buffer, origin);
720
721
722       ...here, and avoided overriding the method in subclasses,
723       but the Sun docs state that this method always will return
724       null, and that overriding is required. Oh, well.
725    */
726  }
727
728  String stringParam()
729  {
730    return "pixel_bits=" + pixel_bits +
731      ", cspace=" + cspace +
732      ", transferType=" + transferType +
733      ", transparency=" + transparency +
734      ", hasAlpha=" + hasAlpha +
735      ", isAlphaPremultiplied=" + isAlphaPremultiplied;
736  }
737
738  public String toString()
739  {
740    return getClass().getName() + "[" + stringParam() + "]";
741  }
742
743  /**
744   * A color model optimized for standard sRGB.
745   */
746  private static class SRGBColorModel
747    extends DirectColorModel
748  {
749
750    SRGBColorModel()
751    {
752      super(32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
753    }
754
755    public int getAlpha(Object inData)
756    {
757      return ((((int[]) inData)[0]) >> 24) & 0xFF;
758    }
759
760    public int getBlue(Object inData)
761    {
762      return ((((int[]) inData)[0])) & 0xFF;
763    }
764
765    public int getGreen(Object inData)
766    {
767      return ((((int[]) inData)[0]) >>  8) & 0xFF;
768    }
769
770    public int getRed(Object inData)
771    {
772      return ((((int[]) inData)[0]) >> 16) & 0xFF;
773    }
774
775    public int getRGB(Object inData)
776    {
777      return ((int[]) inData)[0];
778    }
779
780    public Object getDataElements(int rgb, Object pixel)
781    {
782      if(pixel == null)
783        {
784          pixel = new int[]{rgb};
785        }
786      else
787        {
788          ((int[]) pixel)[0] = rgb;
789        }
790
791      return pixel;
792    }
793  }
794}