001    /* ComponentColorModel.java --
002       Copyright (C) 2000, 2002, 2004  Free Software Foundation
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    
039    package java.awt.image;
040    
041    import gnu.java.awt.Buffers;
042    
043    import java.awt.Point;
044    import java.awt.color.ColorSpace;
045    import java.util.Arrays;
046    
047    public class ComponentColorModel extends ColorModel
048    {
049      // Find sum of all elements of the array.
050      private static int sum(int[] values)
051      {
052        int sum = 0;
053        for (int i=0; i<values.length; i++)
054          sum += values[i];
055        return sum;
056      }
057    
058      // Create an appropriate array of bits, given a colorspace (ie, number of
059      // bands), size of the storage data type, and presence of an alpha band.
060      private static int[] findBits(ColorSpace colorSpace, int transferType,
061                                    boolean hasAlpha)
062      {
063        int[] bits;
064        if (hasAlpha)
065          bits = new int[colorSpace.getNumComponents()+1];
066        else
067          bits = new int[colorSpace.getNumComponents()];
068    
069        Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType));
070    
071        return bits;
072      }
073    
074      public ComponentColorModel(ColorSpace colorSpace, int[] bits,
075                                 boolean hasAlpha,
076                                 boolean isAlphaPremultiplied,
077                                 int transparency, int transferType)
078      {
079        super(sum(bits), bits, colorSpace, hasAlpha, isAlphaPremultiplied,
080              transparency, transferType);
081      }
082    
083      /**
084       * Construct a new ComponentColorModel.
085       *
086       * This constructor makes all bits of each sample significant, so for a
087       * transferType of DataBuffer.BYTE, the bits per sample is 8, etc.  If
088       * both hasAlpha and isAlphaPremultiplied are true, color samples are
089       * assumed to be premultiplied by the alpha component.  Transparency may be
090       * one of OPAQUE, BITMASK, or TRANSLUCENT.
091       *
092       * @param colorSpace The colorspace for this color model.
093       * @param hasAlpha True if there is an alpha component.
094       * @param isAlphaPremultiplied True if colors are already multiplied by
095       * alpha.
096       * @param transparency The type of alpha values.
097       * @param transferType Data type of pixel sample values.
098       * @since 1.4
099       */
100      public ComponentColorModel(ColorSpace colorSpace,
101                                 boolean hasAlpha,
102                                 boolean isAlphaPremultiplied,
103                                 int transparency, int transferType)
104      {
105        this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha,
106             isAlphaPremultiplied, transparency, transferType);
107      }
108    
109      public int getRed(int pixel)
110      {
111        if (getNumComponents()>1) throw new IllegalArgumentException();
112        return (int) getRGBFloat(pixel)[0];
113      }
114    
115      public int getGreen(int pixel)
116      {
117        if (getNumComponents()>1) throw new IllegalArgumentException();
118        return (int) getRGBFloat(pixel)[0];
119      }
120    
121      public int getBlue(int pixel)
122      {
123        if (getNumComponents()>1) throw new IllegalArgumentException();
124        return (int) getRGBFloat(pixel)[0];
125      }
126    
127      public int getAlpha(int pixel)
128      {
129        if (getNumComponents()>1) throw new IllegalArgumentException();
130        int shift = 8 - getComponentSize(getNumColorComponents());
131        if (shift >= 0) return pixel << shift;
132        return pixel >> (-shift);
133      }
134    
135      public int getRGB(int pixel)
136      {
137        float[] rgb = getRGBFloat(pixel);
138        int ret = getRGB(rgb);
139        if (hasAlpha()) ret |= getAlpha(pixel) << 24;
140        return ret;
141      }
142    
143    
144      /* Note, it's OK to pass a to large array to toRGB(). Extra
145         elements are ignored. */
146    
147      private float[] getRGBFloat(int pixel)
148      {
149        float[] data = { pixel };
150        return cspace.toRGB(data);
151      }
152    
153      private float[] getRGBFloat(Object inData)
154      {
155        DataBuffer buffer =
156        Buffers.createBufferFromData(transferType, inData,
157                                     getNumComponents());
158        int colors = getNumColorComponents();
159        float[] data = new float[colors];
160    
161        // FIXME: unpremultiply data that is premultiplied
162        for (int i=0; i<colors; i++)
163          {
164            float maxValue = (1<<getComponentSize(i))-1;
165            data[i] = buffer.getElemFloat(i)/maxValue;
166          }
167        float[] rgb = cspace.toRGB(data);
168        return rgb;
169      }
170    
171      public int getRed(Object inData)
172      {
173        return (int) getRGBFloat(inData)[0]*255;
174      }
175    
176      public int getGreen(Object inData)
177      {
178        return (int) getRGBFloat(inData)[1]*255;
179      }
180    
181      public int getBlue(Object inData)
182      {
183        return (int) getRGBFloat(inData)[2]*255;
184      }
185    
186      public int getAlpha(Object inData)
187      {
188        DataBuffer buffer =
189          Buffers.createBufferFromData(transferType, inData,
190                                       getNumComponents());
191        int shift = 8 - getComponentSize(getNumColorComponents());
192        int alpha = buffer.getElem(getNumColorComponents());
193        if (shift >= 0) return alpha << shift;
194        return alpha >> (-shift);
195      }
196    
197      private int getRGB(float[] rgb)
198      {
199        /* NOTE: We could cast to byte instead of int here. This would
200           avoid bits spilling over from one bit field to
201           another. But, if we assume that floats are in the [0.0,
202           1.0] range, this will never happen anyway. */
203    
204        /* Remember to multiply BEFORE casting to int, otherwise, decimal
205           point data will be lost. */
206        int ret =
207          (((int) (rgb[0]*255F)) << 16) |
208          (((int) (rgb[1]*255F)) <<  8) |
209          (((int) (rgb[2]*255F)) <<  0);
210        return ret;
211      }
212    
213      /**
214       * @param inData pixel data of transferType, as returned by the
215       * getDataElements method in SampleModel.
216       */
217      public int getRGB(Object inData)
218      {
219        float[] rgb = getRGBFloat(inData);
220        int ret = getRGB(rgb);
221        if (hasAlpha()) ret |= getAlpha(inData) << 24;
222        return ret;
223      }
224    
225      public Object getDataElements(int rgb, Object pixel)
226      {
227        // Convert rgb to [0.0, 1.0] sRGB values.
228        float[] rgbFloats = {
229          ((rgb >> 16)&0xff)/255.0F,
230          ((rgb >>  8)&0xff)/255.0F,
231          ((rgb >>  0)&0xff)/255.0F
232        };
233    
234        // Convert from rgb to color space components.
235        float[] data = cspace.fromRGB(rgbFloats);
236        DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
237                                                 getNumComponents());
238        int numColors = getNumColorComponents();
239    
240        if (hasAlpha())
241          {
242            float alpha = ((rgb >> 24)&0xff)/255.0F;
243    
244            /* If color model has alpha and should be premultiplied, multiply
245               color space components with alpha value. */
246            if (isAlphaPremultiplied()) {
247              for (int i=0; i<numColors; i++)
248                data[i] *= alpha;
249            }
250            // Scale the alpha sample to the correct number of bits.
251            alpha *= (1<<(bits[numColors]-1));
252            // Arrange the alpha sample in the output array.
253            buffer.setElemFloat(numColors, alpha);
254          }
255        for (int i=0; i<numColors; i++)
256          {
257            // Scale the color samples to the correct number of bits.
258            float value = data[i]*(1<<(bits[i]-1));
259            // Arrange the color samples in the output array.
260            buffer.setElemFloat(i, value);
261          }
262        return Buffers.getData(buffer);
263      }
264    
265      public int[] getComponents(int pixel, int[] components, int offset)
266      {
267        if (getNumComponents()>1) throw new IllegalArgumentException();
268        if (components == null)
269        components = new int[getNumComponents() + offset];
270        components[offset] = pixel;
271        return components;
272      }
273    
274      public int[] getComponents(Object pixel, int[] components, int offset)
275      {
276        DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
277                                                 getNumComponents());
278        int numComponents = getNumComponents();
279    
280        if (components == null)
281          components = new int[numComponents + offset];
282    
283        for (int i=0; i<numComponents; i++)
284          components[offset++] = buffer.getElem(i);
285    
286        return components;
287      }
288    
289      public int getDataElement(int[] components, int offset)
290      {
291        if (getNumComponents()>1) throw new IllegalArgumentException();
292        return components[offset];
293      }
294    
295      public Object getDataElements(int[] components, int offset, Object obj)
296      {
297        DataBuffer buffer = Buffers.createBuffer(transferType, obj,
298                                                 getNumComponents());
299        int numComponents = getNumComponents();
300    
301        for (int i=0; i<numComponents; i++)
302          buffer.setElem(i, components[offset++]);
303    
304        return Buffers.getData(buffer);
305      }
306    
307      public ColorModel coerceData(WritableRaster raster,
308                                   boolean isAlphaPremultiplied) {
309        if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
310          return this;
311    
312        /* TODO: provide better implementation based on the
313           assumptions we can make due to the specific type of the
314           color model. */
315        coerceDataWorker(raster, isAlphaPremultiplied);
316    
317        return new ComponentColorModel(cspace, hasAlpha, isAlphaPremultiplied,
318                                       transparency, transferType);
319      }
320    
321      public boolean isCompatibleRaster(Raster raster)
322      {
323        return super.isCompatibleRaster(raster);
324        // FIXME: Should we test something more here? (Why override?)
325      }
326    
327      public WritableRaster createCompatibleWritableRaster(int w, int h)
328      {
329        SampleModel sm = createCompatibleSampleModel(w, h);
330        Point origin = new Point(0, 0);
331        return Raster.createWritableRaster(sm, origin);
332      }
333    
334    
335      /**
336       * Creates a <code>SampleModel</code> whose arrangement of pixel
337       * data is compatible to this <code>ColorModel</code>.
338       *
339       * @param w the number of pixels in the horizontal direction.
340       * @param h the number of pixels in the vertical direction.
341       */
342      public SampleModel createCompatibleSampleModel(int w, int h)
343      {
344        int pixelStride, scanlineStride;
345        int[] bandOffsets;
346    
347        pixelStride = getNumComponents();
348        scanlineStride = pixelStride * w;
349    
350        /* We might be able to re-use the same bandOffsets array among
351         * multiple calls to this method. However, this optimization does
352         * not seem worthwile because setting up descriptive data
353         * structures (such as SampleModels) is neglectible in comparision
354         * to shuffling around masses of pixel data.
355         */
356        bandOffsets = new int[pixelStride];
357        for (int i = 0; i < pixelStride; i++)
358          bandOffsets[i] = i;
359    
360        /* FIXME: Think about whether it would make sense to return the
361         * possibly more efficient PixelInterleavedSampleModel for other
362         * transferTypes as well. It seems unlikely that this would break
363         * any user applications, so the Mauve tests on this method
364         * might be too restrictive.
365         */
366        switch (transferType)
367          {
368          case DataBuffer.TYPE_BYTE:
369          case DataBuffer.TYPE_USHORT:
370            return new PixelInterleavedSampleModel(transferType, w, h,
371                                                   pixelStride,
372                                                   scanlineStride,
373                                                   bandOffsets);
374    
375          default:
376            return new ComponentSampleModel(transferType, w, h,
377                                            pixelStride,
378                                            scanlineStride,
379                                            bandOffsets);
380          }
381      }
382    
383    
384      public boolean isCompatibleSampleModel(SampleModel sm)
385      {
386        return
387          (sm instanceof ComponentSampleModel) &&
388          super.isCompatibleSampleModel(sm);
389      }
390    
391      public WritableRaster getAlphaRaster(WritableRaster raster)
392      {
393        if (!hasAlpha()) return null;
394    
395        SampleModel sm = raster.getSampleModel();
396        int[] alphaBand = { sm.getNumBands() - 1 };
397        SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand);
398        DataBuffer buffer = raster.getDataBuffer();
399        Point origin = new Point(0, 0);
400        return Raster.createWritableRaster(alphaModel, buffer, origin);
401      }
402    
403      public boolean equals(Object obj)
404      {
405        if (!(obj instanceof ComponentColorModel)) return false;
406        return super.equals(obj);
407      }
408    }