001/* DirectColorModel.java --
002   Copyright (C) 1999, 2000, 2002, 2004  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;
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 */
052public 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