1 /*
2 * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.imageio;
27
28 import java.awt.Point;
29 import java.awt.Transparency;
30 import java.awt.image.BandedSampleModel;
31 import java.awt.image.BufferedImage;
32 import java.awt.image.ColorModel;
33 import java.awt.color.ColorSpace;
34 import java.awt.image.IndexColorModel;
35 import java.awt.image.ComponentColorModel;
36 import java.awt.image.DataBuffer;
37 import java.awt.image.DirectColorModel;
38 import java.awt.image.MultiPixelPackedSampleModel;
39 import java.awt.image.PixelInterleavedSampleModel;
40 import java.awt.image.SinglePixelPackedSampleModel;
41 import java.awt.image.Raster;
42 import java.awt.image.RenderedImage;
43 import java.awt.image.SampleModel;
44 import java.awt.image.WritableRaster;
45 import java.util.Hashtable;
46
47 /**
48 * A class that allows the format of an image (in particular, its
49 * <code>SampleModel</code> and <code>ColorModel</code>) to be
50 * specified in a convenient manner.
51 *
52 */
53 public class ImageTypeSpecifier {
54
55 /**
56 * The <code>ColorModel</code> to be used as a prototype.
57 */
58 protected ColorModel colorModel;
59
60 /**
61 * A <code>SampleModel</code> to be used as a prototype.
62 */
63 protected SampleModel sampleModel;
64
65 /**
66 * Cached specifiers for all of the standard
67 * <code>BufferedImage</code> types.
68 */
69 private static ImageTypeSpecifier[] BISpecifier;
70
71 // Initialize the standard specifiers
72 static {
73 ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
74
75 BISpecifier =
76 new ImageTypeSpecifier[BufferedImage.TYPE_BYTE_INDEXED + 1];
77
78 BISpecifier[BufferedImage.TYPE_CUSTOM] = null;
79
80 BISpecifier[BufferedImage.TYPE_INT_RGB] =
81 createPacked(sRGB,
82 0x00ff0000,
83 0x0000ff00,
84 0x000000ff,
85 0x0,
86 DataBuffer.TYPE_INT,
87 false);
88
89 BISpecifier[BufferedImage.TYPE_INT_ARGB] =
90 createPacked(sRGB,
91 0x00ff0000,
92 0x0000ff00,
93 0x000000ff,
94 0xff000000,
95 DataBuffer.TYPE_INT,
96 false);
97
98 BISpecifier[BufferedImage.TYPE_INT_ARGB_PRE] =
99 createPacked(sRGB,
100 0x00ff0000,
101 0x0000ff00,
102 0x000000ff,
103 0xff000000,
104 DataBuffer.TYPE_INT,
105 true);
106
107 BISpecifier[BufferedImage.TYPE_INT_BGR] =
108 createPacked(sRGB,
109 0x000000ff,
110 0x0000ff00,
111 0x00ff0000,
112 0x0,
113 DataBuffer.TYPE_INT,
114 false);
115
116 int[] bOffsRGB = { 2, 1, 0 };
117 BISpecifier[BufferedImage.TYPE_3BYTE_BGR] =
118 createInterleaved(sRGB,
119 bOffsRGB,
120 DataBuffer.TYPE_BYTE,
121 false,
122 false);
123
124 int[] bOffsABGR = { 3, 2, 1, 0 };
125 BISpecifier[BufferedImage.TYPE_4BYTE_ABGR] =
126 createInterleaved(sRGB,
127 bOffsABGR,
128 DataBuffer.TYPE_BYTE,
129 true,
130 false);
131
132 BISpecifier[BufferedImage.TYPE_4BYTE_ABGR_PRE] =
133 createInterleaved(sRGB,
134 bOffsABGR,
135 DataBuffer.TYPE_BYTE,
136 true,
137 true);
138
139 BISpecifier[BufferedImage.TYPE_USHORT_565_RGB] =
140 createPacked(sRGB,
141 0xF800,
142 0x07E0,
143 0x001F,
144 0x0,
145 DataBuffer.TYPE_USHORT,
146 false);
147
148 BISpecifier[BufferedImage.TYPE_USHORT_555_RGB] =
149 createPacked(sRGB,
150 0x7C00,
151 0x03E0,
152 0x001F,
153 0x0,
154 DataBuffer.TYPE_USHORT,
155 false);
156
157 BISpecifier[BufferedImage.TYPE_BYTE_GRAY] =
158 createGrayscale(8,
159 DataBuffer.TYPE_BYTE,
160 false);
161
162 BISpecifier[BufferedImage.TYPE_USHORT_GRAY] =
163 createGrayscale(16,
164 DataBuffer.TYPE_USHORT,
165 false);
166
167 BISpecifier[BufferedImage.TYPE_BYTE_BINARY] =
168 createGrayscale(1,
169 DataBuffer.TYPE_BYTE,
170 false);
171
172 BufferedImage bi =
173 new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED);
174 IndexColorModel icm = (IndexColorModel)bi.getColorModel();
175 int mapSize = icm.getMapSize();
176 byte[] redLUT = new byte[mapSize];
177 byte[] greenLUT = new byte[mapSize];
178 byte[] blueLUT = new byte[mapSize];
179 byte[] alphaLUT = new byte[mapSize];
180
181 icm.getReds(redLUT);
182 icm.getGreens(greenLUT);
183 icm.getBlues(blueLUT);
184 icm.getAlphas(alphaLUT);
185
186 BISpecifier[BufferedImage.TYPE_BYTE_INDEXED] =
187 createIndexed(redLUT, greenLUT, blueLUT, alphaLUT,
188 8,
189 DataBuffer.TYPE_BYTE);
190 }
191
192 /**
193 * A constructor to be used by inner subclasses only.
194 */
195 private ImageTypeSpecifier() {}
196
197 /**
198 * Constructs an <code>ImageTypeSpecifier</code> directly
199 * from a <code>ColorModel</code> and a <code>SampleModel</code>.
200 * It is the caller's responsibility to supply compatible
201 * parameters.
202 *
203 * @param colorModel a <code>ColorModel</code>.
204 * @param sampleModel a <code>SampleModel</code>.
205 *
206 * @exception IllegalArgumentException if either parameter is
207 * <code>null</code>.
208 * @exception IllegalArgumentException if <code>sampleModel</code>
209 * is not compatible with <code>colorModel</code>.
210 */
211 public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel) {
212 if (colorModel == null) {
213 throw new IllegalArgumentException("colorModel == null!");
214 }
215 if (sampleModel == null) {
216 throw new IllegalArgumentException("sampleModel == null!");
217 }
218 if (!colorModel.isCompatibleSampleModel(sampleModel)) {
219 throw new IllegalArgumentException
220 ("sampleModel is incompatible with colorModel!");
221 }
222 this.colorModel = colorModel;
223 this.sampleModel = sampleModel;
224 }
225
226 /**
227 * Constructs an <code>ImageTypeSpecifier</code> from a
228 * <code>RenderedImage</code>. If a <code>BufferedImage</code> is
229 * being used, one of the factory methods
230 * <code>createFromRenderedImage</code> or
231 * <code>createFromBufferedImageType</code> should be used instead in
232 * order to get a more accurate result.
233 *
234 * @param image a <code>RenderedImage</code>.
235 *
236 * @exception IllegalArgumentException if the argument is
237 * <code>null</code>.
238 */
239 public ImageTypeSpecifier(RenderedImage image) {
240 if (image == null) {
241 throw new IllegalArgumentException("image == null!");
242 }
243 colorModel = image.getColorModel();
244 sampleModel = image.getSampleModel();
245 }
246
247 // Packed
248
249 static class Packed extends ImageTypeSpecifier {
250 ColorSpace colorSpace;
251 int redMask;
252 int greenMask;
253 int blueMask;
254 int alphaMask;
255 int transferType;
256 boolean isAlphaPremultiplied;
257
258 public Packed(ColorSpace colorSpace,
259 int redMask,
260 int greenMask,
261 int blueMask,
262 int alphaMask, // 0 if no alpha
263 int transferType,
264 boolean isAlphaPremultiplied) {
265 if (colorSpace == null) {
266 throw new IllegalArgumentException("colorSpace == null!");
267 }
268 if (colorSpace.getType() != ColorSpace.TYPE_RGB) {
269 throw new IllegalArgumentException
270 ("colorSpace is not of type TYPE_RGB!");
271 }
272 if (transferType != DataBuffer.TYPE_BYTE &&
273 transferType != DataBuffer.TYPE_USHORT &&
274 transferType != DataBuffer.TYPE_INT) {
275 throw new IllegalArgumentException
276 ("Bad value for transferType!");
277 }
278 if (redMask == 0 && greenMask == 0 &&
279 blueMask == 0 && alphaMask == 0) {
280 throw new IllegalArgumentException
281 ("No mask has at least 1 bit set!");
282 }
283 this.colorSpace = colorSpace;
284 this.redMask = redMask;
285 this.greenMask = greenMask;
286 this.blueMask = blueMask;
287 this.alphaMask = alphaMask;
288 this.transferType = transferType;
289 this.isAlphaPremultiplied = isAlphaPremultiplied;
290
291 int bits = 32;
292 this.colorModel =
293 new DirectColorModel(colorSpace,
294 bits,
295 redMask, greenMask, blueMask,
296 alphaMask, isAlphaPremultiplied,
297 transferType);
298 this.sampleModel = colorModel.createCompatibleSampleModel(1, 1);
299 }
300 }
301
302 /**
303 * Returns a specifier for a packed image format that will use a
304 * <code>DirectColorModel</code> and a packed
305 * <code>SampleModel</code> to store each pixel packed into in a
306 * single byte, short, or int.
307 *
308 * @param colorSpace the desired <code>ColorSpace</code>.
309 * @param redMask a contiguous mask indicated the position of the
310 * red channel.
311 * @param greenMask a contiguous mask indicated the position of the
312 * green channel.
313 * @param blueMask a contiguous mask indicated the position of the
314 * blue channel.
315 * @param alphaMask a contiguous mask indicated the position of the
316 * alpha channel.
317 * @param transferType the desired <code>SampleModel</code> transfer type.
318 * @param isAlphaPremultiplied <code>true</code> if the color channels
319 * will be premultipled by the alpha channel.
320 *
321 * @return an <code>ImageTypeSpecifier</code> with the desired
322 * characteristics.
323 *
324 * @exception IllegalArgumentException if <code>colorSpace</code>
325 * is <code>null</code>.
326 * @exception IllegalArgumentException if <code>colorSpace</code>
327 * is not of type <code>TYPE_RGB</code>.
328 * @exception IllegalArgumentException if no mask has at least 1
329 * bit set.
330 * @exception IllegalArgumentException if
331 * <code>transferType</code> if not one of
332 * <code>DataBuffer.TYPE_BYTE</code>,
333 * <code>DataBuffer.TYPE_USHORT</code>, or
334 * <code>DataBuffer.TYPE_INT</code>.
335 */
336 public static ImageTypeSpecifier
337 createPacked(ColorSpace colorSpace,
338 int redMask,
339 int greenMask,
340 int blueMask,
341 int alphaMask, // 0 if no alpha
342 int transferType,
343 boolean isAlphaPremultiplied) {
344 return new ImageTypeSpecifier.Packed(colorSpace,
345 redMask,
346 greenMask,
347 blueMask,
348 alphaMask, // 0 if no alpha
349 transferType,
350 isAlphaPremultiplied);
351 }
352
353 static ColorModel createComponentCM(ColorSpace colorSpace,
354 int numBands,
355 int dataType,
356 boolean hasAlpha,
357 boolean isAlphaPremultiplied) {
358 int transparency =
359 hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
360
361 int[] numBits = new int[numBands];
362 int bits = DataBuffer.getDataTypeSize(dataType);
363
364 for (int i = 0; i < numBands; i++) {
365 numBits[i] = bits;
366 }
367
368 return new ComponentColorModel(colorSpace,
369 numBits,
370 hasAlpha,
371 isAlphaPremultiplied,
372 transparency,
373 dataType);
374 }
375
376 // Interleaved
377
378 static class Interleaved extends ImageTypeSpecifier {
379 ColorSpace colorSpace;
380 int[] bandOffsets;
381 int dataType;
382 boolean hasAlpha;
383 boolean isAlphaPremultiplied;
384
385 public Interleaved(ColorSpace colorSpace,
386 int[] bandOffsets,
387 int dataType,
388 boolean hasAlpha,
389 boolean isAlphaPremultiplied) {
390 if (colorSpace == null) {
391 throw new IllegalArgumentException("colorSpace == null!");
392 }
393 if (bandOffsets == null) {
394 throw new IllegalArgumentException("bandOffsets == null!");
395 }
396 int numBands = colorSpace.getNumComponents() +
397 (hasAlpha ? 1 : 0);
398 if (bandOffsets.length != numBands) {
399 throw new IllegalArgumentException
400 ("bandOffsets.length is wrong!");
401 }
402 if (dataType != DataBuffer.TYPE_BYTE &&
403 dataType != DataBuffer.TYPE_SHORT &&
404 dataType != DataBuffer.TYPE_USHORT &&
405 dataType != DataBuffer.TYPE_INT &&
406 dataType != DataBuffer.TYPE_FLOAT &&
407 dataType != DataBuffer.TYPE_DOUBLE) {
408 throw new IllegalArgumentException
409 ("Bad value for dataType!");
410 }
411 this.colorSpace = colorSpace;
412 this.bandOffsets = (int[])bandOffsets.clone();
413 this.dataType = dataType;
414 this.hasAlpha = hasAlpha;
415 this.isAlphaPremultiplied = isAlphaPremultiplied;
416
417 this.colorModel =
418 ImageTypeSpecifier.createComponentCM(colorSpace,
419 bandOffsets.length,
420 dataType,
421 hasAlpha,
422 isAlphaPremultiplied);
423
424 int minBandOffset = bandOffsets[0];
425 int maxBandOffset = minBandOffset;
426 for (int i = 0; i < bandOffsets.length; i++) {
427 int offset = bandOffsets[i];
428 minBandOffset = Math.min(offset, minBandOffset);
429 maxBandOffset = Math.max(offset, maxBandOffset);
430 }
431 int pixelStride = maxBandOffset - minBandOffset + 1;
432
433 int w = 1;
434 int h = 1;
435 this.sampleModel =
436 new PixelInterleavedSampleModel(dataType,
437 w, h,
438 pixelStride,
439 w*pixelStride,
440 bandOffsets);
441 }
442
443 public boolean equals(Object o) {
444 if ((o == null) ||
445 !(o instanceof ImageTypeSpecifier.Interleaved)) {
446 return false;
447 }
448
449 ImageTypeSpecifier.Interleaved that =
450 (ImageTypeSpecifier.Interleaved)o;
451
452 if ((!(this.colorSpace.equals(that.colorSpace))) ||
453 (this.dataType != that.dataType) ||
454 (this.hasAlpha != that.hasAlpha) ||
455 (this.isAlphaPremultiplied != that.isAlphaPremultiplied) ||
456 (this.bandOffsets.length != that.bandOffsets.length)) {
457 return false;
458 }
459
460 for (int i = 0; i < bandOffsets.length; i++) {
461 if (this.bandOffsets[i] != that.bandOffsets[i]) {
462 return false;
463 }
464 }
465
466 return true;
467 }
468
469 public int hashCode() {
470 return (super.hashCode() +
471 (4 * bandOffsets.length) +
472 (25 * dataType) +
473 (hasAlpha ? 17 : 18));
474 }
475 }
476
477 /**
478 * Returns a specifier for an interleaved image format that will
479 * use a <code>ComponentColorModel</code> and a
480 * <code>PixelInterleavedSampleModel</code> to store each pixel
481 * component in a separate byte, short, or int.
482 *
483 * @param colorSpace the desired <code>ColorSpace</code>.
484 * @param bandOffsets an array of <code>int</code>s indicating the
485 * offsets for each band.
486 * @param dataType the desired data type, as one of the enumerations
487 * from the <code>DataBuffer</code> class.
488 * @param hasAlpha <code>true</code> if an alpha channel is desired.
489 * @param isAlphaPremultiplied <code>true</code> if the color channels
490 * will be premultipled by the alpha channel.
491 *
492 * @return an <code>ImageTypeSpecifier</code> with the desired
493 * characteristics.
494 *
495 * @exception IllegalArgumentException if <code>colorSpace</code>
496 * is <code>null</code>.
497 * @exception IllegalArgumentException if <code>bandOffsets</code>
498 * is <code>null</code>.
499 * @exception IllegalArgumentException if <code>dataType</code> is
500 * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
501 * @exception IllegalArgumentException if
502 * <code>bandOffsets.length</code> does not equal the number of
503 * color space components, plus 1 if <code>hasAlpha</code> is
504 * <code>true</code>.
505 */
506 public static ImageTypeSpecifier
507 createInterleaved(ColorSpace colorSpace,
508 int[] bandOffsets,
509 int dataType,
510 boolean hasAlpha,
511 boolean isAlphaPremultiplied) {
512 return new ImageTypeSpecifier.Interleaved(colorSpace,
513 bandOffsets,
514 dataType,
515 hasAlpha,
516 isAlphaPremultiplied);
517 }
518
519 // Banded
520
521 static class Banded extends ImageTypeSpecifier {
522 ColorSpace colorSpace;
523 int[] bankIndices;
524 int[] bandOffsets;
525 int dataType;
526 boolean hasAlpha;
527 boolean isAlphaPremultiplied;
528
529 public Banded(ColorSpace colorSpace,
530 int[] bankIndices,
531 int[] bandOffsets,
532 int dataType,
533 boolean hasAlpha,
534 boolean isAlphaPremultiplied) {
535 if (colorSpace == null) {
536 throw new IllegalArgumentException("colorSpace == null!");
537 }
538 if (bankIndices == null) {
539 throw new IllegalArgumentException("bankIndices == null!");
540 }
541 if (bandOffsets == null) {
542 throw new IllegalArgumentException("bandOffsets == null!");
543 }
544 if (bankIndices.length != bandOffsets.length) {
545 throw new IllegalArgumentException
546 ("bankIndices.length != bandOffsets.length!");
547 }
548 if (dataType != DataBuffer.TYPE_BYTE &&
549 dataType != DataBuffer.TYPE_SHORT &&
550 dataType != DataBuffer.TYPE_USHORT &&
551 dataType != DataBuffer.TYPE_INT &&
552 dataType != DataBuffer.TYPE_FLOAT &&
553 dataType != DataBuffer.TYPE_DOUBLE) {
554 throw new IllegalArgumentException
555 ("Bad value for dataType!");
556 }
557 int numBands = colorSpace.getNumComponents() +
558 (hasAlpha ? 1 : 0);
559 if (bandOffsets.length != numBands) {
560 throw new IllegalArgumentException
561 ("bandOffsets.length is wrong!");
562 }
563
564 this.colorSpace = colorSpace;
565 this.bankIndices = (int[])bankIndices.clone();
566 this.bandOffsets = (int[])bandOffsets.clone();
567 this.dataType = dataType;
568 this.hasAlpha = hasAlpha;
569 this.isAlphaPremultiplied = isAlphaPremultiplied;
570
571 this.colorModel =
572 ImageTypeSpecifier.createComponentCM(colorSpace,
573 bankIndices.length,
574 dataType,
575 hasAlpha,
576 isAlphaPremultiplied);
577
578 int w = 1;
579 int h = 1;
580 this.sampleModel = new BandedSampleModel(dataType,
581 w, h,
582 w,
583 bankIndices,
584 bandOffsets);
585 }
586
587 public boolean equals(Object o) {
588 if ((o == null) ||
589 !(o instanceof ImageTypeSpecifier.Banded)) {
590 return false;
591 }
592
593 ImageTypeSpecifier.Banded that =
594 (ImageTypeSpecifier.Banded)o;
595
596 if ((!(this.colorSpace.equals(that.colorSpace))) ||
597 (this.dataType != that.dataType) ||
598 (this.hasAlpha != that.hasAlpha) ||
599 (this.isAlphaPremultiplied != that.isAlphaPremultiplied) ||
600 (this.bankIndices.length != that.bankIndices.length) ||
601 (this.bandOffsets.length != that.bandOffsets.length)) {
602 return false;
603 }
604
605 for (int i = 0; i < bankIndices.length; i++) {
606 if (this.bankIndices[i] != that.bankIndices[i]) {
607 return false;
608 }
609 }
610
611 for (int i = 0; i < bandOffsets.length; i++) {
612 if (this.bandOffsets[i] != that.bandOffsets[i]) {
613 return false;
614 }
615 }
616
617 return true;
618 }
619
620 public int hashCode() {
621 return (super.hashCode() +
622 (3 * bandOffsets.length) +
623 (7 * bankIndices.length) +
624 (21 * dataType) +
625 (hasAlpha ? 19 : 29));
626 }
627 }
628
629 /**
630 * Returns a specifier for a banded image format that will use a
631 * <code>ComponentColorModel</code> and a
632 * <code>BandedSampleModel</code> to store each channel in a
633 * separate array.
634 *
635 * @param colorSpace the desired <code>ColorSpace</code>.
636 * @param bankIndices an array of <code>int</code>s indicating the
637 * bank in which each band will be stored.
638 * @param bandOffsets an array of <code>int</code>s indicating the
639 * starting offset of each band within its bank.
640 * @param dataType the desired data type, as one of the enumerations
641 * from the <code>DataBuffer</code> class.
642 * @param hasAlpha <code>true</code> if an alpha channel is desired.
643 * @param isAlphaPremultiplied <code>true</code> if the color channels
644 * will be premultipled by the alpha channel.
645 *
646 * @return an <code>ImageTypeSpecifier</code> with the desired
647 * characteristics.
648 *
649 * @exception IllegalArgumentException if <code>colorSpace</code>
650 * is <code>null</code>.
651 * @exception IllegalArgumentException if <code>bankIndices</code>
652 * is <code>null</code>.
653 * @exception IllegalArgumentException if <code>bandOffsets</code>
654 * is <code>null</code>.
655 * @exception IllegalArgumentException if the lengths of
656 * <code>bankIndices</code> and <code>bandOffsets</code> differ.
657 * @exception IllegalArgumentException if
658 * <code>bandOffsets.length</code> does not equal the number of
659 * color space components, plus 1 if <code>hasAlpha</code> is
660 * <code>true</code>.
661 * @exception IllegalArgumentException if <code>dataType</code> is
662 * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
663 */
664 public static ImageTypeSpecifier
665 createBanded(ColorSpace colorSpace,
666 int[] bankIndices,
667 int[] bandOffsets,
668 int dataType,
669 boolean hasAlpha,
670 boolean isAlphaPremultiplied) {
671 return new ImageTypeSpecifier.Banded(colorSpace,
672 bankIndices,
673 bandOffsets,
674 dataType,
675 hasAlpha,
676 isAlphaPremultiplied);
677 }
678
679 // Grayscale
680
681 static class Grayscale extends ImageTypeSpecifier {
682 int bits;
683 int dataType;
684 boolean isSigned;
685 boolean hasAlpha;
686 boolean isAlphaPremultiplied;
687
688 public Grayscale(int bits,
689 int dataType,
690 boolean isSigned,
691 boolean hasAlpha,
692 boolean isAlphaPremultiplied)
693 {
694 if (bits != 1 && bits != 2 && bits != 4 &&
695 bits != 8 && bits != 16)
696 {
697 throw new IllegalArgumentException("Bad value for bits!");
698 }
699 if (dataType != DataBuffer.TYPE_BYTE &&
700 dataType != DataBuffer.TYPE_SHORT &&
701 dataType != DataBuffer.TYPE_USHORT)
702 {
703 throw new IllegalArgumentException
704 ("Bad value for dataType!");
705 }
706 if (bits > 8 && dataType == DataBuffer.TYPE_BYTE) {
707 throw new IllegalArgumentException
708 ("Too many bits for dataType!");
709 }
710
711 this.bits = bits;
712 this.dataType = dataType;
713 this.isSigned = isSigned;
714 this.hasAlpha = hasAlpha;
715 this.isAlphaPremultiplied = isAlphaPremultiplied;
716
717 ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
718
719 if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
720 (bits == 16 &&
721 (dataType == DataBuffer.TYPE_SHORT ||
722 dataType == DataBuffer.TYPE_USHORT))) {
723 // Use component color model & sample model
724
725 int numBands = hasAlpha ? 2 : 1;
726 int transparency =
727 hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
728
729
730 int[] nBits = new int[numBands];
731 nBits[0] = bits;
732 if (numBands == 2) {
733 nBits[1] = bits;
734 }
735 this.colorModel =
736 new ComponentColorModel(colorSpace,
737 nBits,
738 hasAlpha,
739 isAlphaPremultiplied,
740 transparency,
741 dataType);
742
743 int[] bandOffsets = new int[numBands];
744 bandOffsets[0] = 0;
745 if (numBands == 2) {
746 bandOffsets[1] = 1;
747 }
748
749 int w = 1;
750 int h = 1;
751 this.sampleModel =
752 new PixelInterleavedSampleModel(dataType,
753 w, h,
754 numBands, w*numBands,
755 bandOffsets);
756 } else {
757 int numEntries = 1 << bits;
758 byte[] arr = new byte[numEntries];
759 for (int i = 0; i < numEntries; i++) {
760 arr[i] = (byte)(i*255/(numEntries - 1));
761 }
762 this.colorModel =
763 new IndexColorModel(bits, numEntries, arr, arr, arr);
764
765 this.sampleModel =
766 new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
767 }
768 }
769 }
770
771 /**
772 * Returns a specifier for a grayscale image format that will pack
773 * pixels of the given bit depth into array elements of
774 * the specified data type.
775 *
776 * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
777 * @param dataType the desired data type, as one of the enumerations
778 * from the <code>DataBuffer</code> class.
779 * @param isSigned <code>true</code> if negative values are to
780 * be represented.
781 *
782 * @return an <code>ImageTypeSpecifier</code> with the desired
783 * characteristics.
784 *
785 * @exception IllegalArgumentException if <code>bits</code> is
786 * not one of 1, 2, 4, 8, or 16.
787 * @exception IllegalArgumentException if <code>dataType</code> is
788 * not one of <code>DataBuffer.TYPE_BYTE</code>,
789 * <code>DataBuffer.TYPE_SHORT</code>, or
790 * <code>DataBuffer.TYPE_USHORT</code>.
791 * @exception IllegalArgumentException if <code>bits</code> is
792 * larger than the bit size of the given <code>dataType</code>.
793 */
794 public static ImageTypeSpecifier
795 createGrayscale(int bits,
796 int dataType,
797 boolean isSigned) {
798 return new ImageTypeSpecifier.Grayscale(bits,
799 dataType,
800 isSigned,
801 false,
802 false);
803 }
804
805 /**
806 * Returns a specifier for a grayscale plus alpha image format
807 * that will pack pixels of the given bit depth into array
808 * elements of the specified data type.
809 *
810 * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
811 * @param dataType the desired data type, as one of the enumerations
812 * from the <code>DataBuffer</code> class.
813 * @param isSigned <code>true</code> if negative values are to
814 * be represented.
815 * @param isAlphaPremultiplied <code>true</code> if the luminance channel
816 * will be premultipled by the alpha channel.
817 *
818 * @return an <code>ImageTypeSpecifier</code> with the desired
819 * characteristics.
820 *
821 * @exception IllegalArgumentException if <code>bits</code> is
822 * not one of 1, 2, 4, 8, or 16.
823 * @exception IllegalArgumentException if <code>dataType</code> is
824 * not one of <code>DataBuffer.TYPE_BYTE</code>,
825 * <code>DataBuffer.TYPE_SHORT</code>, or
826 * <code>DataBuffer.TYPE_USHORT</code>.
827 * @exception IllegalArgumentException if <code>bits</code> is
828 * larger than the bit size of the given <code>dataType</code>.
829 */
830 public static ImageTypeSpecifier
831 createGrayscale(int bits,
832 int dataType,
833 boolean isSigned,
834 boolean isAlphaPremultiplied) {
835 return new ImageTypeSpecifier.Grayscale(bits,
836 dataType,
837 isSigned,
838 true,
839 isAlphaPremultiplied);
840 }
841
842 // Indexed
843
844 static class Indexed extends ImageTypeSpecifier {
845 byte[] redLUT;
846 byte[] greenLUT;
847 byte[] blueLUT;
848 byte[] alphaLUT = null;
849 int bits;
850 int dataType;
851
852 public Indexed(byte[] redLUT,
853 byte[] greenLUT,
854 byte[] blueLUT,
855 byte[] alphaLUT,
856 int bits,
857 int dataType) {
858 if (redLUT == null || greenLUT == null || blueLUT == null) {
859 throw new IllegalArgumentException("LUT is null!");
860 }
861 if (bits != 1 && bits != 2 && bits != 4 &&
862 bits != 8 && bits != 16) {
863 throw new IllegalArgumentException("Bad value for bits!");
864 }
865 if (dataType != DataBuffer.TYPE_BYTE &&
866 dataType != DataBuffer.TYPE_SHORT &&
867 dataType != DataBuffer.TYPE_USHORT &&
868 dataType != DataBuffer.TYPE_INT) {
869 throw new IllegalArgumentException
870 ("Bad value for dataType!");
871 }
872 if ((bits > 8 && dataType == DataBuffer.TYPE_BYTE) ||
873 (bits > 16 && dataType != DataBuffer.TYPE_INT)) {
874 throw new IllegalArgumentException
875 ("Too many bits for dataType!");
876 }
877
878 int len = 1 << bits;
879 if (redLUT.length != len ||
880 greenLUT.length != len ||
881 blueLUT.length != len ||
882 (alphaLUT != null && alphaLUT.length != len)) {
883 throw new IllegalArgumentException("LUT has improper length!");
884 }
885 this.redLUT = (byte[])redLUT.clone();
886 this.greenLUT = (byte[])greenLUT.clone();
887 this.blueLUT = (byte[])blueLUT.clone();
888 if (alphaLUT != null) {
889 this.alphaLUT = (byte[])alphaLUT.clone();
890 }
891 this.bits = bits;
892 this.dataType = dataType;
893
894 if (alphaLUT == null) {
895 this.colorModel = new IndexColorModel(bits,
896 redLUT.length,
897 redLUT,
898 greenLUT,
899 blueLUT);
900 } else {
901 this.colorModel = new IndexColorModel(bits,
902 redLUT.length,
903 redLUT,
904 greenLUT,
905 blueLUT,
906 alphaLUT);
907 }
908
909 if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
910 (bits == 16 &&
911 (dataType == DataBuffer.TYPE_SHORT ||
912 dataType == DataBuffer.TYPE_USHORT))) {
913 int[] bandOffsets = { 0 };
914 this.sampleModel =
915 new PixelInterleavedSampleModel(dataType,
916 1, 1, 1, 1,
917 bandOffsets);
918 } else {
919 this.sampleModel =
920 new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
921 }
922 }
923 }
924
925 /**
926 * Returns a specifier for an indexed-color image format that will pack
927 * index values of the given bit depth into array elements of
928 * the specified data type.
929 *
930 * @param redLUT an array of <code>byte</code>s containing
931 * the red values for each index.
932 * @param greenLUT an array of <code>byte</code>s containing * the
933 * green values for each index.
934 * @param blueLUT an array of <code>byte</code>s containing the
935 * blue values for each index.
936 * @param alphaLUT an array of <code>byte</code>s containing the
937 * alpha values for each index, or <code>null</code> to create a
938 * fully opaque LUT.
939 * @param bits the number of bits in each index.
940 * @param dataType the desired output type, as one of the enumerations
941 * from the <code>DataBuffer</code> class.
942 *
943 * @return an <code>ImageTypeSpecifier</code> with the desired
944 * characteristics.
945 *
946 * @exception IllegalArgumentException if <code>redLUT</code> is
947 * <code>null</code>.
948 * @exception IllegalArgumentException if <code>greenLUT</code> is
949 * <code>null</code>.
950 * @exception IllegalArgumentException if <code>blueLUT</code> is
951 * <code>null</code>.
952 * @exception IllegalArgumentException if <code>bits</code> is
953 * not one of 1, 2, 4, 8, or 16.
954 * @exception IllegalArgumentException if the
955 * non-<code>null</code> LUT parameters do not have lengths of
956 * exactly <code>1 << bits</code>.
957 * @exception IllegalArgumentException if <code>dataType</code> is
958 * not one of <code>DataBuffer.TYPE_BYTE</code>,
959 * <code>DataBuffer.TYPE_SHORT</code>,
960 * <code>DataBuffer.TYPE_USHORT</code>,
961 * or <code>DataBuffer.TYPE_INT</code>.
962 * @exception IllegalArgumentException if <code>bits</code> is
963 * larger than the bit size of the given <code>dataType</code>.
964 */
965 public static ImageTypeSpecifier
966 createIndexed(byte[] redLUT,
967 byte[] greenLUT,
968 byte[] blueLUT,
969 byte[] alphaLUT,
970 int bits,
971 int dataType) {
972 return new ImageTypeSpecifier.Indexed(redLUT,
973 greenLUT,
974 blueLUT,
975 alphaLUT,
976 bits,
977 dataType);
978 }
979
980 /**
981 * Returns an <code>ImageTypeSpecifier</code> that encodes
982 * one of the standard <code>BufferedImage</code> types
983 * (other than <code>TYPE_CUSTOM</code>).
984 *
985 * @param bufferedImageType an int representing one of the standard
986 * <code>BufferedImage</code> types.
987 *
988 * @return an <code>ImageTypeSpecifier</code> with the desired
989 * characteristics.
990 *
991 * @exception IllegalArgumentException if
992 * <code>bufferedImageType</code> is not one of the standard
993 * types, or is equal to <code>TYPE_CUSTOM</code>.
994 *
995 * @see java.awt.image.BufferedImage
996 * @see java.awt.image.BufferedImage#TYPE_INT_RGB
997 * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
998 * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
999 * @see java.awt.image.BufferedImage#TYPE_INT_BGR
1000 * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
1001 * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
1002 * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
1003 * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
1004 * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
1005 * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
1006 * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
1007 * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
1008 * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
1009 */
1010 public static
1011 ImageTypeSpecifier createFromBufferedImageType(int bufferedImageType) {
1012 if (bufferedImageType >= BufferedImage.TYPE_INT_RGB &&
1013 bufferedImageType <= BufferedImage.TYPE_BYTE_INDEXED) {
1014 return BISpecifier[bufferedImageType];
1015 } else if (bufferedImageType == BufferedImage.TYPE_CUSTOM) {
1016 throw new IllegalArgumentException("Cannot create from TYPE_CUSTOM!");
1017 } else {
1018 throw new IllegalArgumentException("Invalid BufferedImage type!");
1019 }
1020 }
1021
1022 /**
1023 * Returns an <code>ImageTypeSpecifier</code> that encodes the
1024 * layout of a <code>RenderedImage</code> (which may be a
1025 * <code>BufferedImage</code>).
1026 *
1027 * @param image a <code>RenderedImage</code>.
1028 *
1029 * @return an <code>ImageTypeSpecifier</code> with the desired
1030 * characteristics.
1031 *
1032 * @exception IllegalArgumentException if <code>image</code> is
1033 * <code>null</code>.
1034 */
1035 public static
1036 ImageTypeSpecifier createFromRenderedImage(RenderedImage image) {
1037 if (image == null) {
1038 throw new IllegalArgumentException("image == null!");
1039 }
1040
1041 if (image instanceof BufferedImage) {
1042 int bufferedImageType = ((BufferedImage)image).getType();
1043 if (bufferedImageType != BufferedImage.TYPE_CUSTOM) {
1044 return BISpecifier[bufferedImageType];
1045 }
1046 }
1047
1048 return new ImageTypeSpecifier(image);
1049 }
1050
1051 /**
1052 * Returns an int containing one of the enumerated constant values
1053 * describing image formats from <code>BufferedImage</code>.
1054 *
1055 * @return an <code>int</code> representing a
1056 * <code>BufferedImage</code> type.
1057 *
1058 * @see java.awt.image.BufferedImage
1059 * @see java.awt.image.BufferedImage#TYPE_CUSTOM
1060 * @see java.awt.image.BufferedImage#TYPE_INT_RGB
1061 * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
1062 * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
1063 * @see java.awt.image.BufferedImage#TYPE_INT_BGR
1064 * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
1065 * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
1066 * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
1067 * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
1068 * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
1069 * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
1070 * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
1071 * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
1072 * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
1073 */
1074 public int getBufferedImageType() {
1075 BufferedImage bi = createBufferedImage(1, 1);
1076 return bi.getType();
1077 }
1078
1079 /**
1080 * Return the number of color components
1081 * specified by this object. This is the same value as returned by
1082 * <code>ColorModel.getNumComponents</code>
1083 *
1084 * @return the number of components in the image.
1085 */
1086 public int getNumComponents() {
1087 return colorModel.getNumComponents();
1088 }
1089
1090 /**
1091 * Return the number of bands
1092 * specified by this object. This is the same value as returned by
1093 * <code>SampleModel.getNumBands</code>
1094 *
1095 * @return the number of bands in the image.
1096 */
1097 public int getNumBands() {
1098 return sampleModel.getNumBands();
1099 }
1100
1101 /**
1102 * Return the number of bits used to represent samples of the given band.
1103 *
1104 * @param band the index of the band to be queried, as an
1105 * int.
1106 *
1107 * @return an int specifying a number of bits.
1108 *
1109 * @exception IllegalArgumentException if <code>band</code> is
1110 * negative or greater than the largest band index.
1111 */
1112 public int getBitsPerBand(int band) {
1113 if (band < 0 | band >= getNumBands()) {
1114 throw new IllegalArgumentException("band out of range!");
1115 }
1116 return sampleModel.getSampleSize(band);
1117 }
1118
1119 /**
1120 * Returns a <code>SampleModel</code> based on the settings
1121 * encapsulated within this object. The width and height of the
1122 * <code>SampleModel</code> will be set to arbitrary values.
1123 *
1124 * @return a <code>SampleModel</code> with arbitrary dimensions.
1125 */
1126 public SampleModel getSampleModel() {
1127 return sampleModel;
1128 }
1129
1130 /**
1131 * Returns a <code>SampleModel</code> based on the settings
1132 * encapsulated within this object. The width and height of the
1133 * <code>SampleModel</code> will be set to the supplied values.
1134 *
1135 * @param width the desired width of the returned <code>SampleModel</code>.
1136 * @param height the desired height of the returned
1137 * <code>SampleModel</code>.
1138 *
1139 * @return a <code>SampleModel</code> with the given dimensions.
1140 *
1141 * @exception IllegalArgumentException if either <code>width</code> or
1142 * <code>height</code> are negative or zero.
1143 * @exception IllegalArgumentException if the product of
1144 * <code>width</code> and <code>height</code> is greater than
1145 * <code>Integer.MAX_VALUE</code>
1146 */
1147 public SampleModel getSampleModel(int width, int height) {
1148 if ((long)width*height > Integer.MAX_VALUE) {
1149 throw new IllegalArgumentException
1150 ("width*height > Integer.MAX_VALUE!");
1151 }
1152 return sampleModel.createCompatibleSampleModel(width, height);
1153 }
1154
1155 /**
1156 * Returns the <code>ColorModel</code> specified by this object.
1157 *
1158 * @return a <code>ColorModel</code>.
1159 */
1160 public ColorModel getColorModel() {
1161 return colorModel;
1162 }
1163
1164 /**
1165 * Creates a <code>BufferedImage</code> with a given width and
1166 * height according to the specification embodied in this object.
1167 *
1168 * @param width the desired width of the returned
1169 * <code>BufferedImage</code>.
1170 * @param height the desired height of the returned
1171 * <code>BufferedImage</code>.
1172 *
1173 * @return a new <code>BufferedImage</code>
1174 *
1175 * @exception IllegalArgumentException if either <code>width</code> or
1176 * <code>height</code> are negative or zero.
1177 * @exception IllegalArgumentException if the product of
1178 * <code>width</code> and <code>height</code> is greater than
1179 * <code>Integer.MAX_VALUE</code>, or if the number of array
1180 * elements needed to store the image is greater than
1181 * <code>Integer.MAX_VALUE</code>.
1182 */
1183 public BufferedImage createBufferedImage(int width, int height) {
1184 try {
1185 SampleModel sampleModel = getSampleModel(width, height);
1186 WritableRaster raster =
1187 Raster.createWritableRaster(sampleModel,
1188 new Point(0, 0));
1189 return new BufferedImage(colorModel, raster,
1190 colorModel.isAlphaPremultiplied(),
1191 new Hashtable());
1192 } catch (NegativeArraySizeException e) {
1193 // Exception most likely thrown from a DataBuffer constructor
1194 throw new IllegalArgumentException
1195 ("Array size > Integer.MAX_VALUE!");
1196 }
1197 }
1198
1199 /**
1200 * Returns <code>true</code> if the given <code>Object</code> is
1201 * an <code>ImageTypeSpecifier</code> and has a
1202 * <code>SampleModel</code> and <code>ColorModel</code> that are
1203 * equal to those of this object.
1204 *
1205 * @param o the <code>Object</code> to be compared for equality.
1206 *
1207 * @return <code>true</code> if the given object is an equivalent
1208 * <code>ImageTypeSpecifier</code>.
1209 */
1210 public boolean equals(Object o) {
1211 if ((o == null) || !(o instanceof ImageTypeSpecifier)) {
1212 return false;
1213 }
1214
1215 ImageTypeSpecifier that = (ImageTypeSpecifier)o;
1216 return (colorModel.equals(that.colorModel)) &&
1217 (sampleModel.equals(that.sampleModel));
1218 }
1219
1220 /**
1221 * Returns the hash code for this ImageTypeSpecifier.
1222 *
1223 * @return a hash code for this ImageTypeSpecifier
1224 */
1225 public int hashCode() {
1226 return (9 * colorModel.hashCode()) + (14 * sampleModel.hashCode());
1227 }
1228 }