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 }