001    /* ImageTypeSpecifier.java --
002       Copyright (C) 2004  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.imageio;
040    
041    import java.awt.Transparency;
042    import java.awt.color.ColorSpace;
043    import java.awt.image.DataBuffer;
044    import java.awt.image.BandedSampleModel;
045    import java.awt.image.BufferedImage;
046    import java.awt.image.ColorModel;
047    import java.awt.image.ComponentColorModel;
048    import java.awt.image.DirectColorModel;
049    import java.awt.image.IndexColorModel;
050    import java.awt.image.MultiPixelPackedSampleModel;
051    import java.awt.image.PixelInterleavedSampleModel;
052    import java.awt.image.RenderedImage;
053    import java.awt.image.SampleModel;
054    
055    /**
056     * ImageTypeSpecifier store the color and sample models associated
057     * with an IIOImage.
058     */
059    public class ImageTypeSpecifier
060    {
061      /**
062       * The image's color model.
063       */
064      protected ColorModel colorModel;
065    
066      /**
067       * The image's sample model.
068       */
069      protected SampleModel sampleModel;
070    
071      /**
072       * Construct an image type specifier with the given models.
073       *
074       * @param colorModel the color model
075       * @param sampleModel the sample model
076       *
077       * @exception IllegalArgumentException if either model argument is
078       * null
079       * @exception IllegalArgumentException if the models are
080       * incompatible with one another
081       */
082      public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel)
083      {
084        if (colorModel == null)
085          throw new IllegalArgumentException("colorModel may not be null");
086    
087        if (sampleModel == null)
088          throw new IllegalArgumentException("sampleModel may not be null");
089    
090        if (!colorModel.isCompatibleSampleModel(sampleModel))
091          throw new IllegalArgumentException
092            ("sample Model not compatible with colorModel");
093        
094        this.colorModel = colorModel;
095        this.sampleModel = sampleModel;
096      }
097    
098      /**
099       * Construct an image type specifier that describes the given
100       * rendered image.
101       *
102       * @param image a rendered image
103       *
104       * @exception IllegalArgumentException if image is null
105       */
106      public ImageTypeSpecifier(RenderedImage image)
107      {
108        if (image == null)
109          throw new IllegalArgumentException("image may not be null");
110        
111        this.colorModel = image.getColorModel();
112        this.sampleModel = image.getSampleModel();
113      }
114    
115      /**
116       * Create an image type specifier for a banded image using a
117       * component color model and a banded sample model.
118       *
119       * @param colorSpace the color space
120       * @param bankIndices the bank indices at which each band will be
121       * stored
122       * @param bankOffsets the starting band offset for each band within
123       * its bank
124       * @param dataType the data type, a DataBuffer constant
125       * @param hasAlpha true if this image type specifier should have an
126       * alpha component, false otherwise
127       * @param isAlphaPremultiplied true if other color components should
128       * be premultiplied by the alpha component, false otherwise
129       *
130       * @return a banded image type specifier
131       *
132       * @exception IllegalArgumentException if any of colorSpace,
133       * bankIndices or bankOffsets is null
134       * @exception IllegalArgumentException if bankIndices and
135       * bankOffsets differ in length
136       * @excpetion IllegalArgumentException if the number of color space
137       * components, including the alpha component if requested, is
138       * different from bandOffsets.length
139       * @exception if dataType is not a valid DataBuffer constant
140       */
141      public static ImageTypeSpecifier createBanded (ColorSpace colorSpace,
142                                                     int[] bankIndices,
143                                                     int[] bankOffsets,
144                                                     int dataType,
145                                                     boolean hasAlpha,
146                                                     boolean isAlphaPremultiplied)
147      {
148        if (colorSpace == null || bankIndices == null || bankOffsets == null)
149          throw new IllegalArgumentException ("null argument");
150    
151        if (bankIndices.length != bankOffsets.length)
152          throw new IllegalArgumentException ("array lengths differ");
153    
154        if (bankOffsets.length != (colorSpace.getNumComponents() + (hasAlpha ? 1 : 0)))
155          throw new IllegalArgumentException ("invalid bankOffsets length");
156    
157        return new ImageTypeSpecifier (new ComponentColorModel (colorSpace,
158                                                                hasAlpha,
159                                                                isAlphaPremultiplied,
160                                                                hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
161                                                                dataType),
162                                       new BandedSampleModel (dataType, 1, 1, 1,
163                                                              bankIndices,
164                                                              bankOffsets));
165      }
166    
167      /**
168       * Create a buffered image with the given dimensions using that has
169       * the characteristics specified by this image type specifier.
170       *
171       * @param width  width of the buffered image, in pixels
172       * @param height the height of the buffered image, in pixels
173       *
174       * @return a buffered image
175       *
176       * @exception IllegalArgumentException if either width or height is
177       * less than or equal to zero
178       * @exception IllegalArgumentException if width * height is greater
179       * than Integer.MAX_VALUE or if the storage required is greater than
180       * Integer.MAX_VALUE
181       */
182      public BufferedImage createBufferedImage (int width, int height)
183      {
184        if (width <= 0 || height <= 0)
185          throw new IllegalArgumentException ("dimension <= 0");
186    
187        // test for overflow
188        if (width * height < Math.min (width, height))
189          throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE");
190    
191        if (width * height * sampleModel.getNumBands() < Math.min (width, height))
192          throw new IllegalArgumentException ("storage required >"
193                                              + " Integer.MAX_VALUE");
194    
195        // FIXME: this is probably wrong:
196        return new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB);
197      }
198    
199      /**
200       * Create an image type specifier that describes the given buffered
201       * image type.
202       *
203       * @param bufferedImageType the buffered image type to represent
204       * with the returned image type specifier
205       *
206       * @return a new image type specifier
207       *
208       * @exception IllegalArgumentException if bufferedImageType is not a
209       * BufferedImage constant or is BufferedImage.TYPE_CUSTOM
210       */
211      public static ImageTypeSpecifier createFromBufferedImageType (int bufferedImageType)
212      {
213        if (bufferedImageType <= BufferedImage.TYPE_CUSTOM
214            || bufferedImageType > BufferedImage.TYPE_BYTE_INDEXED)
215          throw new IllegalArgumentException ("invalid buffered image type");
216    
217        return new ImageTypeSpecifier (new BufferedImage (1, 1, bufferedImageType));
218      }
219    
220      /**
221       * Create an image type specifier that describes the given rendered
222       * image's type.
223       *
224       * @param image the rendered image
225       *
226       * @return a new image type specifier
227       *
228       * @exception IllegalArgumentException if image is null
229       */
230      public static ImageTypeSpecifier createFromRenderedImage (RenderedImage image)
231      {
232        if (image == null)
233          throw new IllegalArgumentException ("image null");
234    
235        return new ImageTypeSpecifier (image);
236      }
237    
238      /**
239       * Create a grayscale image type specifier, given the number of
240       * bits, data type and whether or not the data is signed.
241       *
242       * @param bits the number of bits used to specify a greyscale value
243       * @param dataType a DataBuffer type constant
244       * @param isSigned true if this type specifier should support
245       * negative values, false otherwise
246       *
247       * @return a greyscal image type specifier
248       *
249       * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or 16
250       * @exception IllegalArgumentException if dataType is not
251       * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or
252       * DataBuffer.TYPE_USHORT
253       * @exception if bits is larger than the number of bits in the given
254       * data type
255       */
256      public static ImageTypeSpecifier createGrayscale (int bits, int dataType, boolean isSigned)
257      {
258        return createGrayscale (bits, dataType, isSigned, false);
259      }
260    
261      /**
262       * Create a grayscale image type specifier, given the number of
263       * bits, data type and whether or not the data is signed.
264       *
265       * @param bits the number of bits used to specify a greyscale value
266       * @param dataType a DataBuffer type constant
267       * @param isSigned true if this type specifier should support
268       * negative values, false otherwise
269       *
270       * @return a greyscal image type specifier
271       *
272       * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or
273       * 16
274       * @exception IllegalArgumentException if dataType is not
275       * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or
276       * DataBuffer.TYPE_USHORT
277       * @exception if bits is larger than the number of bits in the given
278       * data type
279       */
280      public static ImageTypeSpecifier createGrayscale (int bits, int dataType,
281                                                        boolean isSigned,
282                                                        boolean isAlphaPremultiplied)
283      {
284        if (bits != 1 && bits != 2 && bits != 4 && bits != 8 && bits != 16)
285          throw new IllegalArgumentException ("invalid bit size");
286    
287        if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT
288            && dataType != DataBuffer.TYPE_USHORT)
289          throw new IllegalArgumentException ("invalid data type");
290    
291        if (dataType == DataBuffer.TYPE_BYTE && bits > 8)
292          throw new IllegalArgumentException ("number of bits too large for data type");
293    
294        // FIXME: this is probably wrong:
295        return new ImageTypeSpecifier (new DirectColorModel (bits, 0xff, 0x0,
296                                                             0x0, 0xff),
297                                       new MultiPixelPackedSampleModel (dataType,
298                                                                        1, 1,
299                                                                        bits));
300      }
301    
302      /**
303       * Return an image type specifier for an image that uses an indexed
304       * colour model where each colour value has the specified number of
305       * bits and type and where the colour tables are those given.
306       *
307       * @param redLUT the red index values
308       * @param greenLUT the green index values
309       * @param blueLUT the blue index values
310       * @param alphaLUT the alpha index values
311       * @param bits the number of bits per index value
312       * @param dataType the type of each index value
313       *
314       * @return an indexed image type specifier
315       *
316       * @exception IllegalArgumentException if any of the colour arrays,
317       * not including alphaLUT, is null
318       * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or
319       * 16
320       * @exception IllegalArgumentException if dataType is not
321       * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or
322       * DataBuffer.TYPE_USHORT
323       * @exception if bits is larger than the number of bits in the given
324       * data type
325       */
326      public static ImageTypeSpecifier createIndexed (byte[] redLUT,
327                                                      byte[] greenLUT,
328                                                      byte[] blueLUT,
329                                                      byte[] alphaLUT,
330                                                      int bits,
331                                                      int dataType)
332      {
333        if (redLUT == null || greenLUT == null || blueLUT == null)
334          throw new IllegalArgumentException ("null colour table");
335    
336        if (bits != 1 && bits != 2 && bits != 4 && bits != 8 && bits != 16)
337          throw new IllegalArgumentException ("invalid bit size");
338    
339        if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT
340            && dataType != DataBuffer.TYPE_USHORT)
341          throw new IllegalArgumentException ("invalid data type");
342    
343        if (dataType == DataBuffer.TYPE_BYTE && bits > 8)
344          throw new IllegalArgumentException ("number of bits too large for data type");
345    
346        // FIXME: this is probably wrong:
347        return new ImageTypeSpecifier (new IndexColorModel (bits, redLUT.length,
348                                                            redLUT, greenLUT, blueLUT,
349                                                            alphaLUT),
350                                       new MultiPixelPackedSampleModel (dataType,
351                                                                        1, 1,
352                                                                        bits));
353      }
354    
355      /**
356       * Create an image type specifier that uses a component colour model
357       * and a pixel interleaved sample model.  Each pixel component will
358       * be stored in a separate value of the given data type.
359       *
360       * @param colorSpace the colour space used by the colour model
361       * @param bandOffsets the starting band offset for each band within
362       * its bank
363       * @param dataType the type of each pixel value
364       * @param hasAlpha true if an alpha channel should be specified,
365       * false otherwise
366       * @param isAlphaPremultiplied true if other colour channels should
367       * be premultiplied by the alpha value, false otherwise
368       *
369       * @return an interleaved image type specifier
370       *
371       * @exception IllegalArgumentException if either colorSpace or
372       * bandOffsets is null
373       * @excpetion IllegalArgumentException if the number of color space
374       * components, including the alpha component if requested, is
375       * different from bandOffsets.length
376       * @exception if dataType is not a valid DataBuffer constant
377       */
378      public static ImageTypeSpecifier createInterleaved (ColorSpace colorSpace,
379                                                          int[] bandOffsets,
380                                                          int dataType,
381                                                          boolean hasAlpha,
382                                                          boolean isAlphaPremultiplied)
383      {
384        if (colorSpace == null || bandOffsets == null)
385          throw new IllegalArgumentException ("null argument");
386    
387        if (bandOffsets.length != (colorSpace.getNumComponents() + (hasAlpha ? 1 : 0)))
388          throw new IllegalArgumentException ("invalid bankOffsets length");
389    
390        return new ImageTypeSpecifier (new ComponentColorModel (colorSpace,
391                                                                hasAlpha,
392                                                                isAlphaPremultiplied,
393                                                                hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
394                                                                dataType),
395                                       new PixelInterleavedSampleModel (dataType, 1, 1, 1, 1,
396                                                                        bandOffsets));
397      }
398    
399      /**
400       * Create an image type specifier using a direct color model and a
401       * packed sample model.  All pixel components will be packed into
402       * one value of the given data type.
403       *
404       * @param colorSpace the color space to use in the color model
405       * @param redMask the bitmask for the red bits 
406       * @param greenMask the bitmask for the green bits 
407       * @param blueMask the bitmask for the blue bits 
408       * @param alphaMask the bitmask for the alpha bits 
409       * @param transferType the data type used to store pixel values
410       * @param isAlphaPremultiplied true if other colour channels should
411       * be premultiplied by the alpha value, false otherwise
412       *
413       * @return a packed image type specifier
414       *
415       * @exception IllegalArgumentException if colorSpace is null
416       * @exception IllegalArgumentException if colorSpace does not have
417       * type ColorSpace.TYPE_RGB
418       * @exception IllegalArgumentException if all masks are 0
419       * @exception IllegalArgumentException if dataType is not
420       * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or
421       * DataBuffer.TYPE_INT
422       */
423      public static ImageTypeSpecifier createPacked (ColorSpace colorSpace,
424                                                     int redMask,
425                                                     int greenMask,
426                                                     int blueMask,
427                                                     int alphaMask,
428                                                     int transferType,
429                                                     boolean isAlphaPremultiplied)
430      {
431        if (colorSpace == null)
432          throw new IllegalArgumentException ("null color space");
433    
434        if (colorSpace.getType() != ColorSpace.TYPE_RGB)
435          throw new IllegalArgumentException ("invalid color space type");
436    
437        if (redMask == 0 && greenMask == 0 && blueMask == 0 && alphaMask == 0)
438          throw new IllegalArgumentException ("no non-zero mask");
439    
440        if (transferType != DataBuffer.TYPE_BYTE && transferType != DataBuffer.TYPE_USHORT
441            && transferType != DataBuffer.TYPE_INT)
442          throw new IllegalArgumentException ("invalid data type");
443    
444        // Assume DataBuffer.TYPE_BYTE.
445        int numBits = 8;
446    
447        if (transferType == DataBuffer.TYPE_SHORT)
448          numBits = 16;
449        else if (transferType == DataBuffer.TYPE_INT)
450          numBits = 32;
451    
452        return new ImageTypeSpecifier (new DirectColorModel (colorSpace,
453                                                             numBits,
454                                                             redMask,
455                                                             greenMask,
456                                                             blueMask,
457                                                             alphaMask,
458                                                             isAlphaPremultiplied,
459                                                             transferType),
460                                       new MultiPixelPackedSampleModel (transferType,
461                                                                        1, 1, numBits));
462      }
463    
464      /**
465       * Get the number of bits per sample in the given band.
466       *
467       * @param band the band from which to get the number of bits
468       *
469       * @return the number of bits in the given band
470       *
471       * @exception IllegalArgumentException if band is out-of-bounds
472       */
473      public int getBitsPerBand (int band)
474      {
475        if (band < 0 || band > sampleModel.getNumBands())
476          throw new IllegalArgumentException ("band out-of-bounds");
477    
478        return sampleModel.getSampleSize (band);
479      }
480    
481      /**
482       * Get the buffered image constant specified by this image type
483       * specifier.
484       *
485       * @return a buffered image constant
486       */
487      public int getBufferedImageType ()
488      {
489        // FIXME:
490        return BufferedImage.TYPE_INT_RGB;
491      }
492    
493      /**
494       * Create a sample model that is compatible with the one specified
495       * by this image type specifier, with the given dimensions.
496       *
497       * @param width the width of the returned sample model
498       * @param height the height of the returned sample model
499       *
500       * @return a sample model compatible with the one in this image type
501       * specifier, with the given dimensions
502       *
503       * @exception IllegalArgumentException if either width or height is
504       * less than or equal to 0
505       * @exception IllegalArgumentException if width * height is greater
506       * than Intere.MAX_VALUE
507       */
508      public SampleModel getSampleModel (int width, int height)
509      {
510        if (width <= 0 || height <= 0)
511          throw new IllegalArgumentException ("invalid dimension");
512    
513        // test for overflow
514        if (width * height < Math.min (width, height))
515          throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE");
516    
517        return sampleModel.createCompatibleSampleModel (width, height);
518      }
519    
520      /**
521       * Get the color model specified by this image type specifier.
522       *
523       * @return the color model
524       */
525      public ColorModel getColorModel()
526      {
527        return colorModel;
528      }
529    
530      /**
531       * Get the number of bands specified by this image type specifier's
532       * sample model.
533       *
534       * @return the number of bands in the sample model
535       */
536      public int getNumBands()
537      {
538        return sampleModel.getNumBands();
539      }
540    
541      /**
542       * Get the number of components specified by this image type
543       * specifier's color model.
544       *
545       * @return the number of color components per pixel
546       */
547      public int getNumComponents()
548      {
549        return colorModel.getNumComponents();
550      }
551    
552      /**
553       * Get the sample model specified by this image type specifier.
554       *
555       * @return the sample model
556       */
557      public SampleModel getSampleModel()
558      {
559        return sampleModel;
560      }
561    }