001    /* DirectColorModel.java --
002       Copyright (C) 1999, 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.Transparency;
045    import java.awt.color.ColorSpace;
046    
047    /**
048     * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
049     * @author C. Brian Jones (cbj@gnu.org)
050     * @author Mark Benvenuto (mcb54@columbia.edu)
051     */
052    public class DirectColorModel extends PackedColorModel
053    {
054      /**
055       * For the color model created with this constructor the pixels
056       * will have fully opaque alpha components with a value of 255.
057       * Each mask should describe a fully contiguous set of bits in the
058       * most likely order of alpha, red, green, blue from the most significant
059       * byte to the least significant byte.
060       * 
061       * @param pixelBits the number of bits wide used for bit size of pixel values
062       * @param rmask the bits describing the red component of a pixel
063       * @param gmask the bits describing the green component of a pixel
064       * @param bmask the bits describing the blue component of a pixel 
065       */
066      public DirectColorModel(int pixelBits, int rmask, int gmask, int bmask)
067      {
068        this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits,
069             rmask, gmask, bmask, 0, 
070             false, // not alpha premultiplied
071             Buffers.smallestAppropriateTransferType(pixelBits) // find type
072             );
073      }
074    
075      /**
076       * For the color model created with this constructor the pixels
077       * will have fully opaque alpha components with a value of 255.
078       * Each mask should describe a fully contiguous set of bits in the
079       * most likely order of red, green, blue from the most significant
080       * byte to the least significant byte.
081       * 
082       * @param pixelBits the number of bits wide used for bit size of pixel values
083       * @param rmask the bits describing the red component of a pixel
084       * @param gmask the bits describing the green component of a pixel
085       * @param bmask the bits describing the blue component of a pixel 
086       * @param amask the bits describing the alpha component of a pixel 
087       */
088      public DirectColorModel(int pixelBits,
089                              int rmask, int gmask, int bmask, int amask)
090      {
091        this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits,
092             rmask, gmask, bmask, amask,
093             false, // not alpha premultiplied
094             Buffers.smallestAppropriateTransferType(pixelBits) // find type
095             );
096      }
097    
098      public DirectColorModel(ColorSpace cspace, int pixelBits,
099                              int rmask, int gmask, int bmask, int amask,
100                              boolean isAlphaPremultiplied,
101                              int transferType)
102      {
103        super(cspace, pixelBits,
104              rmask, gmask, bmask, amask, isAlphaPremultiplied,
105              ((amask == 0) ? Transparency.OPAQUE : Transparency.TRANSLUCENT),
106              transferType);
107      }
108        
109      public final int getRedMask()
110      {
111        return getMask(0);
112      }
113    
114      public final int getGreenMask()
115      {
116        return getMask(1);
117      }
118    
119      public final int getBlueMask()
120      {
121        return getMask(2);
122      }
123    
124      public final int getAlphaMask()
125      {
126        return hasAlpha() ? getMask(3) : 0;
127      }
128    
129      /**
130       * Get the red component of the given pixel.
131       * <br>
132       */
133      public final int getRed(int pixel)
134      {
135        return extractAndNormalizeSample(pixel, 0);
136      }
137    
138      /**
139       * Get the green component of the given pixel.
140       * <br>
141       */
142      public final int getGreen(int pixel)
143      {
144        return extractAndNormalizeSample(pixel, 1);
145      }
146      
147      /**
148       * Get the blue component of the given pixel.
149       * <br>
150       */
151      public final int getBlue(int pixel)
152      {
153        return extractAndNormalizeSample(pixel, 2);
154      }
155    
156      /**
157       * Get the alpha component of the given pixel.
158       * <br>
159       */
160      public final int getAlpha(int pixel)
161      {
162        if (!hasAlpha())
163          return 255;
164        return extractAndScaleSample(pixel, 3);
165      }
166    
167      private int extractAndNormalizeSample(int pixel, int component)
168      {
169        int value = extractAndScaleSample(pixel, component);
170        if (hasAlpha() && isAlphaPremultiplied() && getAlpha(pixel) != 0)
171          value = value*255/getAlpha(pixel);
172        return value;
173      }
174    
175      private int extractAndScaleSample(int pixel, int component)
176      {
177        int field = pixel & getMask(component);
178        int to8BitShift =
179          8 - shifts[component] - getComponentSize(component);
180        return (to8BitShift>0) ?
181          (field << to8BitShift) :
182          (field >>> (-to8BitShift));
183      }
184    
185      /**
186       * Get the RGB color value of the given pixel using the default
187       * RGB color model. 
188       * <br>
189       *
190       * @param pixel a pixel value
191       */
192      public final int getRGB(int pixel) 
193      {
194        /* FIXME: The Sun docs show that this method is overridden, but I
195           don't see any way to improve on the superclass
196           implementation. */
197        return super.getRGB(pixel);
198      }
199    
200      public int getRed(Object inData)
201      {
202        return getRed(getPixelFromArray(inData));
203      }
204    
205      public int getGreen(Object inData)
206      {
207        return getGreen(getPixelFromArray(inData));
208      }
209    
210      public int getBlue(Object inData)
211      {
212        return getBlue(getPixelFromArray(inData));
213      }
214        
215      public int getAlpha(Object inData)
216      {
217        return getAlpha(getPixelFromArray(inData));
218      }
219    
220      public int getRGB(Object inData)
221      {
222        return getRGB(getPixelFromArray(inData));
223      }
224        
225      /**
226       * Converts a normalized pixel int value in the sRGB color
227       * space to an array containing a single pixel of the color space
228       * of the color model.
229       *
230       * <p>This method performs the inverse function of
231       * <code>getRGB(Object inData)</code>.
232       *
233       * @param rgb pixel as a normalized sRGB, 0xAARRGGBB value.
234       *  
235       * @param pixel to avoid needless creation of arrays, an array to
236       * use to return the pixel can be given. If null, a suitable array
237       * will be created.
238       *
239       * @return array of transferType containing a single pixel. The
240       * pixel should be encoded in the natural way of the color model.
241       *
242       * @see #getRGB(Object)
243       */
244      public Object getDataElements(int rgb, Object pixel)
245      {
246        // FIXME: handle alpha multiply
247        
248        int pixelValue = 0;
249        int a = 0;
250        if (hasAlpha()) {
251          a = (rgb >>> 24) & 0xff;
252          pixelValue = valueToField(a, 3, 8);
253        }
254            
255        if (hasAlpha() && isAlphaPremultiplied())
256          {
257            int r, g, b;
258            /* if r=0xff and a=0xff, then resulting
259               value will be (r*a)>>>8 == 0xfe... This seems wrong.
260               We should divide by 255 rather than shifting >>>8 after
261               multiplying.
262               
263               Too bad, shifting is probably less expensive.
264               r = ((rgb >>> 16) & 0xff)*a;
265               g = ((rgb >>>  8) & 0xff)*a;
266               b = ((rgb >>> 0) & 0xff)*a; */
267            /* The r, g, b values we calculate are 16 bit. This allows
268               us to avoid discarding the lower 8 bits obtained if
269               multiplying with the alpha band. */
270            
271            // using 16 bit values
272            r = ((rgb >>> 8) & 0xff00)*a/255;
273            g = ((rgb >>> 0) & 0xff00)*a/255;
274            b = ((rgb <<  8) & 0xff00)*a/255;
275            pixelValue |= 
276              valueToField(r, 0, 16) |  // Red
277              valueToField(g, 1, 16) |  // Green
278              valueToField(b, 2, 16);   // Blue
279          }
280        else
281          {
282            int r, g, b;
283            // using 8 bit values
284            r = (rgb >>> 16) & 0xff;
285            g = (rgb >>>  8) & 0xff;
286            b = (rgb >>>  0) & 0xff;
287            
288            pixelValue |= 
289              valueToField(r, 0, 8) |  // Red
290              valueToField(g, 1, 8) |  // Green
291              valueToField(b, 2, 8);   // Blue
292          }
293        
294        /* In this color model, the whole pixel fits in the first element
295           of the array. */
296        DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 1);
297        buffer.setElem(0, pixelValue);
298        return Buffers.getData(buffer);
299      }
300        
301      /**
302       * Converts a value to the correct field bits based on the
303       * information derived from the field masks.
304       *
305       * @param highBit the position of the most significant bit in the
306       * val parameter.
307       */
308      private int valueToField(int val, int component, int highBit)
309      {
310        int toFieldShift = 
311          getComponentSize(component) + shifts[component] - highBit;
312        int ret = (toFieldShift>0) ?
313          (val << toFieldShift) :
314          (val >>> (-toFieldShift));
315        return ret & getMask(component);
316      }  
317    
318      /**
319       * Converts a 16 bit value to the correct field bits based on the
320       * information derived from the field masks.
321       */
322      private int value16ToField(int val, int component)
323      {
324        int toFieldShift = getComponentSize(component) + shifts[component] - 16;
325        return (toFieldShift>0) ?
326          (val << toFieldShift) :
327          (val >>> (-toFieldShift));
328      }
329    
330      /**
331       * Fills an array with the unnormalized component samples from a
332       * pixel value. I.e. decompose the pixel, but not perform any
333       * color conversion.
334       */
335      public final int[] getComponents(int pixel, int[] components, int offset)
336      {
337        int numComponents = getNumComponents();
338        if (components == null) components = new int[offset + numComponents];
339        
340        for (int b=0; b<numComponents; b++)
341          components[offset++] = (pixel&getMask(b)) >>> shifts[b];
342            
343        return components;
344      }
345    
346      public final int[] getComponents(Object pixel, int[] components,
347                                       int offset)
348      {
349        return getComponents(getPixelFromArray(pixel), components, offset);
350      }
351    
352      /**
353       * Creates a <code>WriteableRaster</code> that has a <code>SampleModel</code>
354       * that is compatible with this <code>ColorModel</code>.
355       *
356       * @param w the width of the writeable raster to create
357       * @param h the height of the writeable raster to create
358       *
359       * @throws IllegalArgumentException if <code>w</code> or <code>h</code>
360       *         is less than or equal to zero
361       */
362      public final WritableRaster createCompatibleWritableRaster(int w, int h)
363      {
364        // Sun also makes this check here.
365        if(w <= 0 || h <= 0)
366          throw new IllegalArgumentException("width (=" + w + ") and height (="
367                                             + h + ") must be > 0");
368    
369        SampleModel sm = createCompatibleSampleModel(w, h);
370        Point origin = new Point(0, 0);
371        return Raster.createWritableRaster(sm, origin);     
372      }
373    
374      public int getDataElement(int[] components, int offset)
375      {
376        int numComponents = getNumComponents();
377        int pixelValue = 0;
378        
379        for (int c=0; c<numComponents; c++)
380          pixelValue |= (components[offset++] << shifts[c]) & getMask(c);
381    
382        return pixelValue;
383      }  
384    
385      public Object getDataElements(int[] components, int offset, Object obj)
386      {
387        /* In this color model, the whole pixel fits in the first element
388           of the array. */
389        int pixelValue = getDataElement(components, offset);
390    
391        DataBuffer buffer = Buffers.createBuffer(transferType, obj, 1);
392        buffer.setElem(0, pixelValue);
393        return Buffers.getData(buffer);
394      }
395        
396      public final ColorModel coerceData (WritableRaster raster,
397                                          boolean isAlphaPremultiplied)
398      {
399        if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
400          return this;
401            
402        /* TODO: provide better implementation based on the
403           assumptions we can make due to the specific type of the
404           color model. */
405        coerceDataWorker(raster, isAlphaPremultiplied);
406        
407        return new DirectColorModel(cspace, pixel_bits, getRedMask(),
408                                    getGreenMask(), getBlueMask(), getAlphaMask(),
409                                    isAlphaPremultiplied, transferType);
410      } 
411    
412      public boolean isCompatibleRaster(Raster raster)
413      {
414        /* FIXME: the Sun docs say this method is overridden here, 
415           but I don't see any way to improve upon the implementation
416           in ColorModel. */
417        return super.isCompatibleRaster(raster);
418      }
419    
420      String stringParam()
421      {
422        return super.stringParam() +
423          ", redMask=" + Integer.toHexString(getRedMask()) +
424          ", greenMask=" + Integer.toHexString(getGreenMask()) +
425          ", blueMask=" + Integer.toHexString(getBlueMask()) +
426          ", alphaMask=" + Integer.toHexString(getAlphaMask());
427      }
428    
429      public String toString()
430      {
431        /* FIXME: Again, docs say override, but how do we improve upon the
432           superclass implementation? */
433        return super.toString();
434      }
435    }
436