1 /* ComponentColorModel.java --
2 Copyright (C) 2000, 2002, 2004 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.awt.image;
41 import gnu.java.awt.Buffers;
43 import java.awt.Point;
44 import java.awt.color.ColorSpace;
45 import java.util.Arrays;
47 public class ComponentColorModel extends ColorModel
49 // Find sum of all elements of the array.
50 private static int sum(int[] values)
53 for (int i=0; i<values.length; i++)
58 // Create an appropriate array of bits, given a colorspace (ie, number of
59 // bands), size of the storage data type, and presence of an alpha band.
60 private static int[] findBits(ColorSpace colorSpace, int transferType,
65 bits = new int[colorSpace.getNumComponents()+1];
67 bits = new int[colorSpace.getNumComponents()];
69 Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType));
74 public ComponentColorModel(ColorSpace colorSpace, int[] bits,
76 boolean isAlphaPremultiplied,
77 int transparency, int transferType)
79 super(sum(bits), bits, colorSpace, hasAlpha, isAlphaPremultiplied,
80 transparency, transferType);
84 * Construct a new ComponentColorModel.
86 * This constructor makes all bits of each sample significant, so for a
87 * transferType of DataBuffer.BYTE, the bits per sample is 8, etc. If
88 * both hasAlpha and isAlphaPremultiplied are true, color samples are
89 * assumed to be premultiplied by the alpha component. Transparency may be
90 * one of OPAQUE, BITMASK, or TRANSLUCENT.
92 * @param colorSpace The colorspace for this color model.
93 * @param hasAlpha True if there is an alpha component.
94 * @param isAlphaPremultiplied True if colors are already multiplied by
96 * @param transparency The type of alpha values.
97 * @param transferType Data type of pixel sample values.
100 public ComponentColorModel(ColorSpace colorSpace,
102 boolean isAlphaPremultiplied,
103 int transparency, int transferType)
105 this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha,
106 isAlphaPremultiplied, transparency, transferType);
109 public int getRed(int pixel)
111 if (getNumComponents()>1) throw new IllegalArgumentException();
112 return (int) getRGBFloat(pixel)[0];
115 public int getGreen(int pixel)
117 if (getNumComponents()>1) throw new IllegalArgumentException();
118 return (int) getRGBFloat(pixel)[0];
121 public int getBlue(int pixel)
123 if (getNumComponents()>1) throw new IllegalArgumentException();
124 return (int) getRGBFloat(pixel)[0];
127 public int getAlpha(int pixel)
129 if (getNumComponents()>1) throw new IllegalArgumentException();
130 int shift = 8 - getComponentSize(getNumColorComponents());
131 if (shift >= 0) return pixel << shift;
132 return pixel >> (-shift);
135 public int getRGB(int pixel)
137 float[] rgb = getRGBFloat(pixel);
138 int ret = getRGB(rgb);
139 if (hasAlpha()) ret |= getAlpha(pixel) << 24;
144 /* Note, it's OK to pass a to large array to toRGB(). Extra
145 elements are ignored. */
147 private float[] getRGBFloat(int pixel)
149 float[] data = { pixel };
150 return cspace.toRGB(data);
153 private float[] getRGBFloat(Object inData)
156 Buffers.createBufferFromData(transferType, inData,
158 int colors = getNumColorComponents();
159 float[] data = new float[colors];
161 // FIXME: unpremultiply data that is premultiplied
162 for (int i=0; i<colors; i++)
164 float maxValue = (1<<getComponentSize(i))-1;
165 data[i] = buffer.getElemFloat(i)/maxValue;
167 float[] rgb = cspace.toRGB(data);
171 public int getRed(Object inData)
173 return (int) getRGBFloat(inData)[0]*255;
176 public int getGreen(Object inData)
178 return (int) getRGBFloat(inData)[1]*255;
181 public int getBlue(Object inData)
183 return (int) getRGBFloat(inData)[2]*255;
186 public int getAlpha(Object inData)
189 Buffers.createBufferFromData(transferType, inData,
191 int shift = 8 - getComponentSize(getNumColorComponents());
192 int alpha = buffer.getElem(getNumColorComponents());
193 if (shift >= 0) return alpha << shift;
194 return alpha >> (-shift);
197 private int getRGB(float[] rgb)
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. */
204 /* Remember to multiply BEFORE casting to int, otherwise, decimal
205 point data will be lost. */
207 (((int) (rgb[0]*255F)) << 16) |
208 (((int) (rgb[1]*255F)) << 8) |
209 (((int) (rgb[2]*255F)) << 0);
214 * @param inData pixel data of transferType, as returned by the
215 * getDataElements method in SampleModel.
217 public int getRGB(Object inData)
219 float[] rgb = getRGBFloat(inData);
220 int ret = getRGB(rgb);
221 if (hasAlpha()) ret |= getAlpha(inData) << 24;
225 public Object getDataElements(int rgb, Object pixel)
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
234 // Convert from rgb to color space components.
235 float[] data = cspace.fromRGB(rgbFloats);
236 DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
238 int numColors = getNumColorComponents();
242 float alpha = ((rgb >> 24)&0xff)/255.0F;
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++)
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);
255 for (int i=0; i<numColors; i++)
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);
262 return Buffers.getData(buffer);
265 public int[] getComponents(int pixel, int[] components, int offset)
267 if (getNumComponents()>1) throw new IllegalArgumentException();
268 if (components == null)
269 components = new int[getNumComponents() + offset];
270 components[offset] = pixel;
274 public int[] getComponents(Object pixel, int[] components, int offset)
276 DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
278 int numComponents = getNumComponents();
280 if (components == null)
281 components = new int[numComponents + offset];
283 for (int i=0; i<numComponents; i++)
284 components[offset++] = buffer.getElem(i);
289 public int getDataElement(int[] components, int offset)
291 if (getNumComponents()>1) throw new IllegalArgumentException();
292 return components[offset];
295 public Object getDataElements(int[] components, int offset, Object obj)
297 DataBuffer buffer = Buffers.createBuffer(transferType, obj,
299 int numComponents = getNumComponents();
301 for (int i=0; i<numComponents; i++)
302 buffer.setElem(i, components[offset++]);
304 return Buffers.getData(buffer);
307 public ColorModel coerceData(WritableRaster raster,
308 boolean isAlphaPremultiplied) {
309 if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
312 /* TODO: provide better implementation based on the
313 assumptions we can make due to the specific type of the
315 coerceDataWorker(raster, isAlphaPremultiplied);
317 return new ComponentColorModel(cspace, hasAlpha, isAlphaPremultiplied,
318 transparency, transferType);
321 public boolean isCompatibleRaster(Raster raster)
323 return super.isCompatibleRaster(raster);
324 // FIXME: Should we test something more here? (Why override?)
327 public WritableRaster createCompatibleWritableRaster(int w, int h)
329 SampleModel sm = createCompatibleSampleModel(w, h);
330 Point origin = new Point(0, 0);
331 return Raster.createWritableRaster(sm, origin);
336 * Creates a <code>SampleModel</code> whose arrangement of pixel
337 * data is compatible to this <code>ColorModel</code>.
339 * @param w the number of pixels in the horizontal direction.
340 * @param h the number of pixels in the vertical direction.
342 public SampleModel createCompatibleSampleModel(int w, int h)
344 int pixelStride, scanlineStride;
347 pixelStride = getNumComponents();
348 scanlineStride = pixelStride * w;
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.
356 bandOffsets = new int[pixelStride];
357 for (int i = 0; i < pixelStride; i++)
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.
366 switch (transferType)
368 case DataBuffer.TYPE_BYTE:
369 case DataBuffer.TYPE_USHORT:
370 return new PixelInterleavedSampleModel(transferType, w, h,
376 return new ComponentSampleModel(transferType, w, h,
384 public boolean isCompatibleSampleModel(SampleModel sm)
387 (sm instanceof ComponentSampleModel) &&
388 super.isCompatibleSampleModel(sm);
391 public WritableRaster getAlphaRaster(WritableRaster raster)
393 if (!hasAlpha()) return null;
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);
403 public boolean equals(Object obj)
405 if (!(obj instanceof ComponentColorModel)) return false;
406 return super.equals(obj);