Source code: org/ydp/jai/FloatDoubleColorModel.java
1 /*
2 * The contents of this file are subject to the JAVA ADVANCED IMAGING
3 * SAMPLE INPUT-OUTPUT CODECS AND WIDGET HANDLING SOURCE CODE License
4 * Version 1.0 (the "License"); You may not use this file except in
5 * compliance with the License. You may obtain a copy of the License at
6 * http://www.sun.com/software/imaging/JAI/index.html
7 *
8 * Software distributed under the License is distributed on an "AS IS"
9 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
10 * the License for the specific language governing rights and limitations
11 * under the License.
12 *
13 * The Original Code is JAVA ADVANCED IMAGING SAMPLE INPUT-OUTPUT CODECS
14 * AND WIDGET HANDLING SOURCE CODE.
15 * The Initial Developer of the Original Code is: Sun Microsystems, Inc..
16 * Portions created by: _______________________________________
17 * are Copyright (C): _______________________________________
18 * All Rights Reserved.
19 * Contributor(s): _______________________________________
20 */
21
22 package org.ydp.jai;
23
24 import java.awt.Point;
25 import java.awt.Transparency;
26 import java.awt.color.ColorSpace;
27 import java.awt.image.ColorModel;
28 import java.awt.image.ComponentColorModel;
29 import java.awt.image.ComponentSampleModel;
30 import java.awt.image.DataBuffer;
31 import java.awt.image.Raster;
32 import java.awt.image.SampleModel;
33 import java.awt.image.WritableRaster;
34
35 /**
36 * A <code>ColorModel</code> class that works with pixel values that
37 * represent color and alpha information as separate samples, using
38 * float or double elements. This class can be used with an arbitrary
39 * <code>ColorSpace</code>. The number of color samples in the pixel
40 * values must be same as the number of color components in the
41 * <code>ColorSpace</code>. There may be a single alpha sample.
42 *
43 * <p> Sample values are taken as ranging from 0.0 to 1.0; that is,
44 * when converting to 8-bit RGB, a multiplication by 255 is performed
45 * and values outside of the range 0-255 are clamped at the closest
46 * endpoint.
47 *
48 * <p> For maximum efficiency, pixel data being interpreted by this
49 * class should be in the sRGB color space. This will result in
50 * only the trivial conversion (scaling by 255 and dividing by any
51 * premultiplied alpha) to be performed. Other color spaces require
52 * more general conversions.
53 *
54 * <p> For those methods that use a primitive array pixel
55 * representation of type <code>transferType</code>, the array length
56 * is the same as the number of color and alpha samples. Color
57 * samples are stored first in the array followed by the alpha sample,
58 * if present. The order of the color samples is specified by the
59 * <code>ColorSpace</code>. Typically, this order reflects the name
60 * of the color space type. For example, for <code>TYPE_RGB</code>,
61 * index 0 corresponds to red, index 1 to green, and index 2 to blue.
62 * The transfer types supported are
63 * <code>DataBuffer.TYPE_FLOAT</code>,
64 * <code>DataBuffer.TYPE_DOUBLE</code>.
65 *
66 * <p> The translation from pixel values to color/alpha components for
67 * display or processing purposes is a one-to-one correspondence of
68 * samples to components.
69 *
70 * <p> Methods that use a single int pixel representation throw an
71 * <code>IllegalArgumentException</code>.
72 *
73 * <p> A <code>FloatDoubleColorModel</code> can be used in
74 * conjunction with a <code>ComponentSampleModelJAI</code>.
75 *
76 * @see ColorModel
77 * @see ColorSpace
78 * @see ComponentSampleModel
79 * @see ComponentSampleModelJAI
80 */
81 public class FloatDoubleColorModel extends ComponentColorModel {
82
83 ColorSpace colorSpace;
84 int colorSpaceType;
85 int numColorComponents;
86 int numComponents;
87 int transparency;
88 boolean hasAlpha;
89 boolean isAlphaPremultiplied;
90
91 private static int[] bitsHelper(int transferType,
92 ColorSpace colorSpace,
93 boolean hasAlpha) {
94 int numBits = (transferType == DataBuffer.TYPE_FLOAT) ? 32 : 64;
95 int numComponents = colorSpace.getNumComponents();
96 if (hasAlpha) {
97 ++numComponents;
98 }
99 int[] bits = new int[numComponents];
100 for (int i = 0; i < numComponents; i++) {
101 bits[i] = numBits;
102 }
103
104 return bits;
105 }
106
107 /**
108 * Constructs a <code>ComponentColorModel</code> from the
109 * specified parameters. Color components will be in the specified
110 * <code>ColorSpace</code>. <code>hasAlpha</code> indicates
111 * whether alpha information is present. If <code>hasAlpha</code>
112 * is true, then the boolean <code>isAlphaPremultiplied</code>
113 * specifies how to interpret color and alpha samples in pixel
114 * values. If the boolean is <code>true</code>, color samples are
115 * assumed to have been multiplied by the alpha sample. The
116 * <code>transparency</code> specifies what alpha values can be
117 * represented by this color model. The <code>transferType</code>
118 * is the type of primitive array used to represent pixel values.
119 *
120 * @param colorSpace The <code>ColorSpace</code> associated with
121 * this color model.
122 * @param hasAlpha If true, this color model supports alpha.
123 * @param isAlphaPremultiplied If true, alpha is premultiplied.
124 * @param transparency Specifies what alpha values can be represented
125 * by this color model.
126 * @param transferType Specifies the type of primitive array used to
127 * represent pixel values, one of
128 * DataBuffer.TYPE_FLOAT or TYPE_DOUBLE.
129 * @throws IllegalArgumentException If the transfer type is not
130 * DataBuffer.TYPE_FLOAT or TYPE_DOUBLE.
131 *
132 * @see ColorSpace
133 * @see java.awt.Transparency
134 */
135 public FloatDoubleColorModel(ColorSpace colorSpace,
136 boolean hasAlpha,
137 boolean isAlphaPremultiplied,
138 int transparency,
139 int transferType) {
140 super(colorSpace, bitsHelper(transferType, colorSpace, hasAlpha),
141 hasAlpha, isAlphaPremultiplied,
142 transparency,
143 transferType);
144
145 if (transferType != DataBuffer.TYPE_FLOAT &&
146 transferType != DataBuffer.TYPE_DOUBLE) {
147 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel0"));
148 }
149
150 this.colorSpace = colorSpace;
151 this.colorSpaceType = colorSpace.getType();
152 this.numComponents =
153 this.numColorComponents = colorSpace.getNumComponents();
154 if (hasAlpha) {
155 ++numComponents;
156 }
157 this.transparency = transparency;
158 this.hasAlpha = hasAlpha;
159 this.isAlphaPremultiplied = isAlphaPremultiplied;
160 }
161
162 /**
163 * Throws an <code>IllegalArgumentException</code>, since pixel
164 * values for this <code>ColorModel</code> are not conveniently
165 * representable as a single <code>int</code>.
166 */
167 public int getRed(int pixel) {
168 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel1"));
169 }
170
171 /**
172 * Throws an <code>IllegalArgumentException</code>, since pixel
173 * values for this <code>ColorModel</code> are not conveniently
174 * representable as a single <code>int</code>.
175 */
176 public int getGreen(int pixel) {
177 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel2"));
178 }
179
180 /**
181 * Throws an <code>IllegalArgumentException</code>, since pixel
182 * values for this <code>ColorModel</code> are not conveniently
183 * representable as a single <code>int</code>.
184 */
185 public int getBlue(int pixel) {
186 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel3"));
187 }
188
189 /**
190 * Throws an <code>IllegalArgumentException</code>, since pixel
191 * values for this <code>ColorModel</code> are not conveniently
192 * representable as a single <code>int</code>.
193 */
194 public int getAlpha(int pixel) {
195 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel4"));
196 }
197
198 /**
199 * Throws an <code>IllegalArgumentException</code>, since pixel
200 * values for this <code>ColorModel</code> are not conveniently
201 * representable as a single <code>int</code>.
202 */
203 public int getRGB(int pixel) {
204 throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel5"));
205 }
206
207 private int clamp(float value) {
208 // Ensure NaN maps to 0
209 return (value >= 0.0F) ? ((value > 255.0F) ? 255 : (int)value) : 0;
210 }
211
212 private int clamp(double value) {
213 // Ensure NaN maps to 0
214 return (value >= 0.0) ? ((value > 255.0) ? 255 : (int)value) : 0;
215 }
216
217 private int getSample(Object inData, int sample) {
218 boolean needAlpha = (hasAlpha && isAlphaPremultiplied);
219 int type = colorSpaceType;
220
221 boolean is_sRGB = colorSpace.isCS_sRGB();
222
223 if (type == ColorSpace.TYPE_GRAY) {
224 sample = 0;
225 is_sRGB = true;
226 }
227
228 if (is_sRGB) {
229 if (transferType == DataBuffer.TYPE_FLOAT) {
230 float[] fdata = (float[])inData;
231 float fsample = fdata[sample]*255;
232 if (needAlpha) {
233 float falp = fdata[numColorComponents];
234 return clamp(fsample/falp);
235 } else {
236 return clamp(fsample);
237 }
238 } else {
239 double[] ddata = (double[])inData;
240 double dsample = ddata[sample]*255.0;
241 if (needAlpha) {
242 double dalp = ddata[numColorComponents];
243 return clamp(dsample/dalp);
244 } else {
245 return clamp(dsample);
246 }
247 }
248 }
249
250 // Not TYPE_GRAY or TYPE_RGB ColorSpace
251 float[] norm;
252 float[] rgb;
253 if (transferType == DataBuffer.TYPE_FLOAT) {
254 float[] fdata = (float[])inData;
255 if (needAlpha) {
256 float falp = fdata[numColorComponents];
257 norm = new float[numColorComponents];
258 for (int i = 0; i < numColorComponents; i++) {
259 norm[i] = fdata[i]/falp;
260 }
261 rgb = colorSpace.toRGB(norm);
262 return (int)(rgb[sample]*falp*255); // Why multiply by falp?
263 } else {
264 rgb = colorSpace.toRGB(fdata);
265 return (int)(rgb[sample]*255);
266 }
267 } else {
268 double[] ddata = (double[])inData;
269 norm = new float[numColorComponents];
270 if (needAlpha) {
271 double dalp = ddata[numColorComponents];
272 for (int i = 0; i < numColorComponents; i++) {
273 norm[i] = (float)(ddata[i]/dalp);
274 }
275 rgb = colorSpace.toRGB(norm);
276 return (int)(rgb[sample]*dalp*255);
277 } else {
278 for (int i = 0; i < numColorComponents; i++) {
279 norm[i] = (float)ddata[i];
280 }
281 rgb = colorSpace.toRGB(norm);
282 return (int)(rgb[sample]*255);
283 }
284 }
285 }
286
287 /**
288 * Returns the red color component for the specified pixel, scaled
289 * from 0 to 255 in the default RGB ColorSpace, sRGB. A color
290 * conversion is done if necessary. The <code>pixel</code> value
291 * is specified by an array of data elements of type
292 * <code>transferType</code> passed in as an object reference. The
293 * returned value will be a non pre-multiplied value. If the alpha
294 * is premultiplied, this method divides it out before returning
295 * the value (if the alpha value is 0, the red value will be 0).
296 *
297 * @param inData The pixel from which you want to get the red
298 * color component, specified by an array of data elements of type
299 * <code>transferType</code>.
300 *
301 * @return The red color component for the specified pixel, as an
302 * int.
303 *
304 * @throws ClassCastException If <code>inData</code> is not a
305 * primitive array of type <code>transferType</code>.
306 * @throws ArrayIndexOutOfBoundsException if <code>inData</code>
307 * is not large enough to hold a pixel value for this
308 * <code>ColorModel</code>.
309 */
310 public int getRed(Object inData) {
311 return getSample(inData, 0);
312 }
313
314 /**
315 * Returns the green color component for the specified pixel, scaled
316 * from 0 to 255 in the default RGB ColorSpace, sRGB. A color
317 * conversion is done if necessary. The <code>pixel</code> value
318 * is specified by an array of data elements of type
319 * <code>transferType</code> passed in as an object reference. The
320 * returned value will be a non pre-multiplied value. If the alpha
321 * is premultiplied, this method divides it out before returning
322 * the value (if the alpha value is 0, the green value will be 0).
323 *
324 * @param inData The pixel from which you want to get the green
325 * color component, specified by an array of data elements of type
326 * <code>transferType</code>.
327 *
328 * @return The green color component for the specified pixel, as an
329 * int.
330 *
331 * @throws ClassCastException If <code>inData</code> is not a
332 * primitive array of type <code>transferType</code>.
333 * @throws ArrayIndexOutOfBoundsException if <code>inData</code>
334 * is not large enough to hold a pixel value for this
335 * <code>ColorModel</code>.
336 */
337 public int getGreen(Object inData) {
338 return getSample(inData, 1);
339 }
340
341 /**
342 * Returns the blue color component for the specified pixel, scaled
343 * from 0 to 255 in the default RGB ColorSpace, sRGB. A color
344 * conversion is done if necessary. The <code>pixel</code> value
345 * is specified by an array of data elements of type
346 * <code>transferType</code> passed in as an object reference. The
347 * returned value will be a non pre-multiplied value. If the alpha
348 * is premultiplied, this method divides it out before returning
349 * the value (if the alpha value is 0, the blue value will be 0).
350 *
351 * @param inData The pixel from which you want to get the blue
352 * color component, specified by an array of data elements of type
353 * <code>transferType</code>.
354 *
355 * @return The blue color component for the specified pixel, as an
356 * int.
357 *
358 * @throws ClassCastException If <code>inData</code> is not a
359 * primitive array of type <code>transferType</code>.
360 * @throws ArrayIndexOutOfBoundsException if <code>inData</code>
361 * is not large enough to hold a pixel value for this
362 * <code>ColorModel</code>.
363 */
364 public int getBlue(Object inData) {
365 return getSample(inData, 2);
366 }
367
368 /**
369 * Returns the alpha component for the specified pixel, scaled
370 * from 0 to 255. The pixel value is specified by an array of
371 * data elements of type <code>transferType</code> passed in as an
372 * object reference. If the <code>ColorModel</code> does not have
373 * alpha, 255 is returned.
374 *
375 * @param inData The pixel from which you want to get the alpha
376 * component, specified by an array of data elements of type
377 * <code>transferType</code>.
378 *
379 * @return The alpha component for the specified pixel, as an int.
380 *
381 * @throws NullPointerException if <code>inData</code> is
382 * <code>null</code> and the <code>colorModel</code> has alpha.
383 * @throws ClassCastException If <code>inData</code> is not a
384 * primitive array of type <code>transferType</code> and the
385 * <code>ColorModel</code> has alpha.
386 * @throws ArrayIndexOutOfBoundsException if <code>inData</code>
387 * is not large enough to hold a pixel value for this
388 * <code>ColorModel</code> and the <code>ColorModel</code> has
389 * alpha.
390 */
391 public int getAlpha(Object inData) {
392 if (hasAlpha == false) {
393 return 255;
394 }
395
396 if (transferType == DataBuffer.TYPE_FLOAT) {
397 float[] fdata = (float[])inData;
398 return (int)(fdata[numColorComponents]*255.0F);
399 } else {
400 double[] ddata = (double[])inData;
401 return (int)(ddata[numColorComponents]*255.0);
402 }
403 }
404
405 /**
406 * Returns the color/alpha components for the specified pixel in
407 * the default RGB color model format. A color conversion is done
408 * if necessary. The pixel value is specified by an array of data
409 * elements of type <code>transferType</code> passed in as an
410 * object reference. The returned value is in a non
411 * pre-multiplied format. If the alpha is premultiplied, this
412 * method divides it out of the color components (if the alpha
413 * value is 0, the color values will be 0).
414 *
415 * @param inData The pixel from which you want to get the
416 * color/alpha components, specified by an array of data elements
417 * of type <code>transferType</code>.
418 *
419 * @return The color/alpha components for the specified pixel, as an int.
420 *
421 * @throws ClassCastException If <code>inData</code> is not a
422 * primitive array of type <code>transferType</code>.
423 * @throws ArrayIndexOutOfBoundsException if <code>inData</code>
424 * is not large enough to hold a pixel value for this
425 * <code>ColorModel</code>.
426 */
427 public int getRGB(Object inData) {
428 boolean needAlpha = (hasAlpha && isAlphaPremultiplied);
429 int alpha = 255;
430 int red, green, blue;
431
432 if (colorSpace.isCS_sRGB()) {
433 if (transferType == DataBuffer.TYPE_FLOAT) {
434 float[] fdata = (float[])inData;
435 float fred = fdata[0];
436 float fgreen = fdata[1];
437 float fblue = fdata[2];
438 float fscale = 255.0F;
439 if (needAlpha) {
440 float falpha = fdata[3];
441 fscale /= falpha;
442 alpha = clamp(255.0F*falpha);
443 }
444
445 red = clamp(fred*fscale);
446 green = clamp(fgreen*fscale);
447 blue = clamp(fblue*fscale);
448 } else {
449 double[] ddata = (double[])inData;
450 double dred = ddata[0];
451 double dgreen = ddata[1];
452 double dblue = ddata[2];
453 double dscale = 255.0;
454 if (needAlpha) {
455 double dalpha = ddata[3];
456 dscale /= dalpha;
457 alpha = clamp(255.0*dalpha);
458 }
459
460 red = clamp(dred*dscale);
461 green = clamp(dgreen*dscale);
462 blue = clamp(dblue*dscale);
463 }
464 } else if (colorSpaceType == ColorSpace.TYPE_GRAY) {
465 if (transferType == DataBuffer.TYPE_FLOAT) {
466 float[] fdata = (float[])inData;
467 float fgray = fdata[0];
468 if (needAlpha) {
469 float falp = fdata[1];
470 red = green = blue = clamp(fgray*255.0F/falp);
471 alpha = clamp(255.0F*falp);
472 } else {
473 red = green = blue = clamp(fgray*255.0F);
474 }
475 } else {
476 double[] ddata = (double[])inData;
477 double dgray = ddata[0];
478 if (needAlpha) {
479 double dalp = ddata[1];
480 red = green = blue = clamp(dgray*255.0/dalp);
481 alpha = clamp(255.0*dalp);
482 } else {
483 red = green = blue = clamp(dgray*255.0);
484 }
485 }
486 } else {
487 // Not Gray or sRGB
488 float[] norm;
489 float[] rgb;
490 if (transferType == DataBuffer.TYPE_FLOAT) {
491 float[] fdata = (float[])inData;
492 if (needAlpha) {
493 float falp = fdata[numColorComponents];
494 float invfalp = 1.0F/falp;
495 norm = new float[numColorComponents];
496 for (int i = 0; i < numColorComponents; i++) {
497 norm[i] = fdata[i]*invfalp;
498 }
499 alpha = clamp(255.0F*falp);
500 } else {
501 norm = fdata;
502 }
503 } else {
504 double[] ddata = (double[])inData;
505 norm = new float[numColorComponents];
506 if (needAlpha) {
507 double dalp = ddata[numColorComponents];
508 double invdalp = 1.0/dalp;
509 for (int i = 0; i < numColorComponents; i++) {
510 norm[i] = (float)(ddata[i]*invdalp);
511 }
512 alpha = clamp(255.0*dalp);
513 } else {
514 for (int i = 0; i < numColorComponents; i++) {
515 norm[i] = (float)ddata[i];
516 }
517 }
518 }
519
520 // Perform color conversion
521 rgb = colorSpace.toRGB(norm);
522
523 red = clamp(rgb[0]*255.0F);
524 green = clamp(rgb[1]*255.0F);
525 blue = clamp(rgb[2]*255.0F);
526 }
527
528 return (alpha << 24) | (red << 16) | (green << 8) | blue;
529 }
530
531
532 /**
533 * Returns a data element array representation of a pixel in this
534 * <code>ColorModel</code>, given an integer pixel representation
535 * in the default RGB color model. This array can then be passed
536 * to the <code>setDataElements</code> method of a
537 * <code>WritableRaster</code> object. If the <code>pixel</code>
538 * parameter is null, a new array is allocated.
539 *
540 * @param rgb An ARGB value packed into an int.
541 * @param pixel The float or double array representation of the pixel.
542 *
543 * @throws ClassCastException If <code>pixel</code> is not null and
544 * is not a primitive array of type <code>transferType</code>.
545 *
546 * @throws ArrayIndexOutOfBoundsException If <code>pixel</code> is
547 * not large enough to hold a pixel value for this
548 * <code>ColorModel</code>.
549 */
550 public Object getDataElements(int rgb, Object pixel) {
551 if (transferType == DataBuffer.TYPE_FLOAT) {
552 float[] floatPixel;
553
554 if (pixel == null) {
555 floatPixel = new float[numComponents];
556 } else {
557 if (!(pixel instanceof float[])) {
558 throw new ClassCastException(JaiI18N.getString("FloatDoubleColorModel7"));
559 }
560 floatPixel = (float[])pixel;
561 if (floatPixel.length < numComponents) {
562 throw new ArrayIndexOutOfBoundsException(JaiI18N.getString("FloatDoubleColorModel8"));
563 }
564 }
565
566 float inv255 = 1.0F/255.0F;
567 if (colorSpace.isCS_sRGB()) {
568 int alp = (rgb >> 24) & 0xff;
569 int red = (rgb >> 16) & 0xff;
570 int grn = (rgb >> 8) & 0xff;
571 int blu = (rgb ) & 0xff;
572 float norm = inv255;
573 if (isAlphaPremultiplied) {
574 norm *= alp;
575 }
576 floatPixel[0] = red*norm;
577 floatPixel[1] = grn*norm;
578 floatPixel[2] = blu*norm;
579 if (hasAlpha) {
580 floatPixel[3] = alp*inv255;
581 }
582 } else if (colorSpaceType == ColorSpace.TYPE_GRAY) {
583 float gray = ((((rgb>>16)&0xff)*(.299F*inv255)) +
584 (((rgb>>8) &0xff)*(.587F*inv255)) +
585 (((rgb) &0xff)*(.114F*inv255)));
586
587 floatPixel[0] = gray;
588
589 if (hasAlpha) {
590 int alpha = (rgb>>24) & 0xff;
591 floatPixel[1] = alpha*inv255;
592 }
593 } else {
594 // Need to convert the color
595 float[] norm = new float[3];
596 norm[0] = ((rgb>>16) & 0xff)*inv255;
597 norm[1] = ((rgb>>8) & 0xff)*inv255;
598 norm[2] = ((rgb) & 0xff)*inv255;
599
600 norm = colorSpace.fromRGB(norm);
601 for (int i = 0; i < numColorComponents; i++) {
602 floatPixel[i] = norm[i];
603 }
604 if (hasAlpha) {
605 int alpha = (rgb>>24) & 0xff;
606 floatPixel[numColorComponents] = alpha*inv255;
607 }
608 }
609
610 return floatPixel;
611 } else { // transferType == DataBuffer.TYPE_DOUBLE
612 double[] doublePixel;
613
614 if (pixel == null) {
615 doublePixel = new double[numComponents];
616 } else {
617 if (!(pixel instanceof double[])) {
618 throw new ClassCastException(JaiI18N.getString("FloatDoubleColorModel7"));
619 }
620 doublePixel = (double[])pixel;
621 if (doublePixel.length < numComponents) {
622 throw new ArrayIndexOutOfBoundsException(JaiI18N.getString("FloatDoubleColorModel8"));
623 }
624 }
625
626 double inv255 = 1.0/255.0;
627 if (colorSpace.isCS_sRGB()) {
628 int alp = (rgb>>24) & 0xff;
629 int red = (rgb>>16) & 0xff;
630 int grn = (rgb>>8) & 0xff;
631 int blu = (rgb) & 0xff;
632 double norm = inv255;
633 if (isAlphaPremultiplied) {
634 norm *= alp;
635 }
636 doublePixel[0] = red*norm;
637 doublePixel[1] = grn*norm;
638 doublePixel[2] = blu*norm;
639 if (hasAlpha) {
640 doublePixel[3] = alp*inv255;
641 }
642 } else if (colorSpaceType == ColorSpace.TYPE_GRAY) {
643 double gray = ((((rgb>>16) & 0xff)*(.299*inv255)) +
644 (((rgb>>8) & 0xff)*(.587*inv255)) +
645 (((rgb) & 0xff)*(.114*inv255)));
646
647 doublePixel[0] = gray;
648
649 if (hasAlpha) {
650 int alpha = (rgb>>24) & 0xff;
651 doublePixel[1] = alpha*inv255;
652 }
653 } else {
654 float inv255F = 1.0F/255.0F;
655
656 // Need to convert the color, need data in float form
657 float[] norm = new float[3];
658 norm[0] = ((rgb>>16) & 0xff)*inv255F;
659 norm[1] = ((rgb>>8) & 0xff)*inv255F;
660 norm[2] = ((rgb) & 0xff)*inv255F;
661
662 norm = colorSpace.fromRGB(norm);
663 for (int i = 0; i < numColorComponents; i++) {
664 doublePixel[i] = (double)norm[i];
665 }
666 if (hasAlpha) {
667 int alpha = (rgb>>24) & 0xff;
668 doublePixel[numColorComponents] = alpha*inv255;
669 }
670 }
671
672 return doublePixel;
673 }
674 }
675
676 /**
677 * Throws an <code>IllegalArgumentException</code>, since pixel
678 * values for this <code>ColorModel</code> are not conveniently
679 * representable as a single <code>int</code>.
680 */
681 public int[] getComponents(int pixel, int[] components, int offset) {
682 throw new
683 IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9"));
684 }
685
686 /**
687 * Throws an <code>IllegalArgumentException</code> since
688 * the pixel values cannot be placed into an <code>int</code> array.
689 */
690 public int[] getComponents(Object pixel, int[] components, int offset) {
691 throw new
692 IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9"));
693 }
694
695 /**
696 * Throws an <code>IllegalArgumentException</code>, since pixel
697 * values for this <code>ColorModel</code> are not conveniently
698 * representable as a single <code>int</code>.
699 */
700 public int getDataElement(int[] components, int offset) {
701 throw new
702 IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9"));
703 }
704
705 /**
706 * Returns a data element array representation of a pixel in this
707 * <code>ColorModel</code>, given an array of unnormalized
708 * color/alpha components. This array can then be passed to the
709 * <code>setDataElements</code> method of a
710 * <code>WritableRaster</code> object.
711 *
712 * @param components An array of unnormalized color/alpha
713 * components.
714 * @param offset The integer offset into the
715 * <code>components</code> array.
716 * @param obj The object in which to store the data element array
717 * representation of the pixel. If <code>obj</code> variable is
718 * null, a new array is allocated. If <code>obj</code> is not
719 * null, it must be a primitive array of type
720 * <code>transferType</code>. An
721 * <code>ArrayIndexOutOfBoundsException</code> is thrown if
722 * <code>obj</code> is not large enough to hold a pixel value for
723 * this <code>ColorModel</code>.
724 *
725 * @return The data element array representation of a pixel
726 * in this <code>ColorModel</code>.
727 *
728 * @throws IllegalArgumentException If the components array
729 * is not large enough to hold all the color and alpha components
730 * (starting at offset).
731 * @throws ClassCastException If <code>obj</code> is not null and
732 * is not a primitive array of type <code>transferType</code>.
733 * @throws ArrayIndexOutOfBoundsException If <code>obj</code> is
734 * not large enough to hold a pixel value for this
735 * <code>ColorModel</code>.
736 */
737 public Object getDataElements(int[] components, int offset, Object obj) {
738 if ((components.length-offset) < numComponents) {
739 throw new IllegalArgumentException(numComponents + " " +
740 JaiI18N.getString("FloatDoubleColorModel10"));
741 }
742 if (transferType == DataBuffer.TYPE_FLOAT) {
743 float[] pixel;
744 if (obj == null) {
745 pixel = new float[components.length];
746 } else {
747 pixel = (float[])obj;
748 }
749 for (int i=0; i < numComponents; i++) {
750 pixel[i] = (float)(components[offset + i]);
751 }
752
753 return pixel;
754 } else {
755 double[] pixel;
756 if (obj == null) {
757 pixel = new double[components.length];
758 } else {
759 pixel = (double[])obj;
760 }
761 for (int i=0; i < numComponents; i++) {
762 pixel[i] = (double)(components[offset + i]);
763 }
764
765 return pixel;
766 }
767 }
768
769 /**
770 * Forces the raster data to match the state specified in the
771 * <code>isAlphaPremultiplied</code> variable, assuming the data
772 * is currently correctly described by this <code>ColorModel</code>.
773 * It may multiply or divide the color raster data by alpha, or
774 * do nothing if the data is in the correct state. If the data needs
775 * to be coerced, this method also returns an instance of
776 * <code>FloatDoubleColorModel</code> with
777 * the <code>isAlphaPremultiplied</code> flag set appropriately.
778 *
779 * @throws IllegalArgumentException if transfer type of
780 * <code>raster</code> is not the same as that of this
781 * <code>FloatDoubleColorModel</code>.
782 */
783 public ColorModel coerceData (WritableRaster raster,
784 boolean isAlphaPremultiplied) {
785 if ((hasAlpha == false) ||
786 (this.isAlphaPremultiplied == isAlphaPremultiplied))
787 {
788 // Nothing to do
789 return this;
790 }
791
792 int w = raster.getWidth();
793 int h = raster.getHeight();
794 int aIdx = raster.getNumBands() - 1;
795 int rminX = raster.getMinX();
796 int rY = raster.getMinY();
797 int rX;
798
799 if (raster.getTransferType() != transferType) {
800 throw new IllegalArgumentException(
801 JaiI18N.getString("FloatDoubleColorModel6"));
802 }
803
804 if (isAlphaPremultiplied) {
805 switch (transferType) {
806 case DataBuffer.TYPE_FLOAT: {
807 float pixel[] = null;
808 for (int y = 0; y < h; y++, rY++) {
809 rX = rminX;
810 for (int x = 0; x < w; x++, rX++) {
811 pixel = (float[])raster.getDataElements(rX, rY,
812 pixel);
813 float fAlpha = pixel[aIdx];
814 if (fAlpha != 0) {
815 for (int c=0; c < aIdx; c++) {
816 pixel[c] *= fAlpha;
817 }
818 raster.setDataElements(rX, rY, pixel);
819 }
820 }
821 }
822 }
823 break;
824 case DataBuffer.TYPE_DOUBLE: {
825 double pixel[] = null;
826 for (int y = 0; y < h; y++, rY++) {
827 rX = rminX;
828 for (int x = 0; x < w; x++, rX++) {
829 pixel = (double[])raster.getDataElements(rX, rY,
830 pixel);
831 double dAlpha = pixel[aIdx];
832 if (dAlpha != 0) {
833 for (int c=0; c < aIdx; c++) {
834 pixel[c] *= dAlpha;
835 }
836 raster.setDataElements(rX, rY, pixel);
837 }
838 }
839 }
840 }
841 break;
842 }
843 }
844 else {
845 // We are premultiplied and want to divide it out
846 switch (transferType) {
847 case DataBuffer.TYPE_FLOAT: {
848 for (int y = 0; y < h; y++, rY++) {
849 rX = rminX;
850 for (int x = 0; x < w; x++, rX++) {
851 float pixel[] = null;
852 pixel = (float[])raster.getDataElements(rX, rY,
853 pixel);
854 float fAlpha = pixel[aIdx];
855 if (fAlpha != 0) {
856 float invFAlpha = 1.0F/fAlpha;
857 for (int c=0; c < aIdx; c++) {
858 pixel[c] *= invFAlpha;
859 }
860 }
861 raster.setDataElements(rX, rY, pixel);
862 }
863 }
864 }
865 break;
866 case DataBuffer.TYPE_DOUBLE: {
867 for (int y = 0; y < h; y++, rY++) {
868 rX = rminX;
869 for (int x = 0; x < w; x++, rX++) {
870 double pixel[] = null;
871 pixel = (double[])raster.getDataElements(rX, rY,
872 pixel);
873 double dAlpha = pixel[aIdx];
874 if (dAlpha != 0) {
875 double invDAlpha = 1.0/dAlpha;
876 for (int c=0; c < aIdx; c++) {
877 pixel[c] *= invDAlpha;
878 }
879 }
880 raster.setDataElements(rX, rY, pixel);
881 }
882 }
883 }
884 break;
885 }
886 }
887
888 // Return a new color model
889 return new FloatDoubleColorModel(colorSpace, hasAlpha,
890 isAlphaPremultiplied, transparency,
891 transferType);
892 }
893
894 /**
895 * Returns <code>true</code> if the supplied <code>Raster</code>'s
896 * <code>SampleModel</code> is compatible with this
897 * <code>FloatDoubleColorModel</code>.
898 *
899 * @param raster a <code>Raster</code>to be checked for compatibility.
900 */
901 public boolean isCompatibleRaster(Raster raster) {
902 SampleModel sm = raster.getSampleModel();
903 return isCompatibleSampleModel(sm);
904 }
905
906 /**
907 * Creates a <code>WritableRaster</code> with the specified width
908 * and height, that has a data layout (<code>SampleModel</code>)
909 * compatible with this <code>ColorModel</code>. The returned
910 * <code>WritableRaster</code>'s <code>SampleModel</code> will be
911 * an instance of <code>ComponentSampleModel</code>.
912 *
913 * @param w The width of the <code>WritableRaster</code> you want
914 * to create.
915 * @param h The height of the <code>WritableRaster</code> you want
916 * to create.
917 *
918 * @return A <code>WritableRaster</code> that is compatible with
919 * this <code>ColorModel</code>.
920 *
921 * @see WritableRaster
922 * @see SampleModel
923 */
924 public WritableRaster createCompatibleWritableRaster(int w, int h) {
925 SampleModel sm = createCompatibleSampleModel(w, h);
926 return RasterFactory.createWritableRaster(sm, new Point(0, 0));
927 }
928
929 /**
930 * Creates a <code>SampleModel</code> with the specified width and
931 * height that has a data layout compatible with this
932 * <code>ColorModel</code>. The returned <code>SampleModel</code>
933 * will be an instance of <code>ComponentSampleModel</code>.
934 *
935 * @param w The width of the <code>SampleModel</code> you want to create.
936 * @param h The height of the <code>SampleModel</code> you want to create.
937 *
938 * @return A <code>SampleModel</code> that is compatible with this
939 * <code>ColorModel</code>.
940 *
941 * @see SampleModel
942 * @see ComponentSampleModel
943 */
944 public SampleModel createCompatibleSampleModel(int w, int h) {
945 int[] bandOffsets = new int[numComponents];
946 for (int i = 0; i < numComponents; i++) {
947 bandOffsets[i] = i;
948 }
949 return new ComponentSampleModelJAI(transferType,
950 w, h,
951 numComponents,
952 w*numComponents,
953 bandOffsets);
954 }
955
956 /**
957 * Checks whether or not the specified <code>SampleModel</code> is
958 * compatible with this <code>ColorModel</code>. A
959 * <code>SampleModel</code> is compatible if it is an instance of
960 * <code>ComponentSampleModel</code>, has the sample number of
961 * bands as the total nomber of components (including alpha) in
962 * the <code>ColorSpace</code> used by this
963 * <code>ColorModel</code>, and has the same data type (float or
964 * double) as this <code>ColorModel</code>.
965 *
966 * @param sm The <code>SampleModel</code> to test for compatibility.
967 *
968 * @return <code>true</code> if the <code>SampleModel</code> is
969 * compatible with this <code>ColorModel</code>,
970 * <code>false</code> if it is not.
971 *
972 * @see SampleModel
973 * @see ComponentSampleModel
974 */
975 public boolean isCompatibleSampleModel(SampleModel sm) {
976 if (sm instanceof ComponentSampleModel) {
977 if (sm.getNumBands() != getNumComponents()) {
978 return false;
979 }
980 if (sm.getDataType() != transferType) {
981 return false;
982 }
983 return true;
984 } else {
985 return false;
986 }
987 }
988
989 /** Returns a String containing the values of all valid fields. */
990 public String toString() {
991 return "FloatDoubleColorModel: " + super.toString();
992 }
993 }