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