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 }