001/*
002 * $RCSfile: RawImageInputStream.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.1 $
042 * $Date: 2005/02/11 05:01:20 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.stream;
046
047import java.awt.Dimension;
048import java.awt.Transparency;
049import java.awt.color.ColorSpace;
050import java.awt.color.ICC_ColorSpace;
051import java.awt.color.ICC_Profile;
052import java.awt.image.ColorModel;
053import java.awt.image.ComponentColorModel;
054import java.awt.image.ComponentSampleModel;
055import java.awt.image.DirectColorModel;
056import java.awt.image.IndexColorModel;
057import java.awt.image.MultiPixelPackedSampleModel;
058import java.awt.image.SampleModel;
059import java.awt.image.SinglePixelPackedSampleModel;
060import java.io.IOException;
061import java.io.InputStream;
062import java.net.URL;
063import java.nio.ByteOrder;
064import java.util.StringTokenizer;
065
066import javax.imageio.ImageTypeSpecifier;
067import javax.imageio.stream.IIOByteBuffer;
068import javax.imageio.stream.ImageInputStream;
069import javax.xml.parsers.DocumentBuilder;
070import javax.xml.parsers.DocumentBuilderFactory;
071import javax.xml.parsers.ParserConfigurationException;
072
073import org.w3c.dom.Document;
074import org.w3c.dom.NamedNodeMap;
075import org.w3c.dom.Node;
076import org.w3c.dom.NodeList;
077import org.xml.sax.SAXException;
078
079import com.github.jaiimageio.impl.common.ImageUtil;
080
081/**
082 * This class defines the content of the <code>ImageInputStream</code>
083 * containing several raw images with the same image type: the number of
084 * the images, the image type, the offset for the first sample of each image,
085 * and the image size information.
086 *
087 * <p><code>ImageInputStream</code> methods are not commented individually.
088 * These methods merely forward the call to the <code>ImageInputStream</code>
089 * specified when the <code>RawImageInputStream</code> is constructed.</p>
090 */
091public class RawImageInputStream implements ImageInputStream {
092    private static final String[] preDefinedColorSpaces =
093        new String[]{"GRAY", "sRGB", "LINEAR_RGB", "PYCC", "CIEXYZ"};
094
095    private static final int[]  preDefinedTypes = new int[]{
096        ColorSpace.CS_GRAY, ColorSpace.CS_sRGB, ColorSpace.CS_LINEAR_RGB,
097        ColorSpace.CS_PYCC, ColorSpace.CS_CIEXYZ
098    };
099
100    /** Gets the attribute from the node. */
101    private static String getAttribute(Node node, String name) {
102        NamedNodeMap map = node.getAttributes();
103        node = map.getNamedItem(name);
104        return (node != null) ? node.getNodeValue() : null;
105    }
106
107    /** Gets the boolean type attribute. */
108    private static boolean getBoolean(Node node, String name) {
109        String s = getAttribute(node, name);
110        return (s == null) ? false : (new Boolean(s)).booleanValue();
111    }
112
113    /** Gets the integer type attribute. */
114    private static int getInt(Node node, String name) {
115        String s = getAttribute(node, name);
116        return (s == null) ? 0 : (new Integer(s)).intValue();
117    }
118
119    /** Gets the integer type attribute. */
120    private static byte[] getByteArray(Node node, String name) {
121        String s = getAttribute(node, name);
122        if (s == null)
123            return null;
124        StringTokenizer token = new StringTokenizer(s);
125        int count = token.countTokens();
126        if (count == 0)
127            return null;
128
129        byte[] buf = new byte[count];
130        int i = 0;
131        while(token.hasMoreElements()) {
132            buf[i++] = new Byte(token.nextToken()).byteValue();
133        }
134        return buf;
135    }
136
137    /** Gets the integer type attribute. */
138    private static int[] getIntArray(Node node, String name) {
139        String s = getAttribute(node, name);
140        if (s == null)
141            return null;
142
143        StringTokenizer token = new StringTokenizer(s);
144        int count = token.countTokens();
145        if (count == 0)
146            return null;
147
148        int[] buf = new int[count];
149        int i = 0;
150        while(token.hasMoreElements()) {
151            buf[i++] = new Integer(token.nextToken()).intValue();
152        }
153        return buf;
154    }
155
156    private static int getTransparency(String s) {
157        if ("BITMASK".equals(s))
158            return Transparency.BITMASK;
159        else if ("OPAQUE".equals(s))
160            return Transparency.OPAQUE;
161        else if ("TRANSLUCENT".equals(s))
162            return Transparency.TRANSLUCENT;
163        else return 0;
164    }
165
166    private static ColorSpace getColorSpace(Node node) throws IOException {
167        NodeList nodes = node.getChildNodes();
168        for (int i = 0; i < nodes.getLength(); i++) {
169            Node child = nodes.item(i);
170            if ("colorSpace".equals(child.getNodeName())) {
171                String s = child.getNodeValue();
172                for (int j = 0; j < preDefinedColorSpaces.length; j++) {
173                    if (preDefinedColorSpaces[j].equals(s))
174                        return ColorSpace.getInstance(preDefinedTypes[j]);
175                }
176
177                InputStream stm = new URL(s).openStream();
178
179                ColorSpace cp = new ICC_ColorSpace(ICC_Profile.getInstance(stm));
180                stm.close();
181                return cp;
182            }
183        }
184        return null;
185    }
186
187    /** The wrapperred <code>ImageInputStream</code>. */
188    private ImageInputStream source;
189
190    /** The image type for all the images in the stream. */
191    private ImageTypeSpecifier type;
192
193    /** The position of the first sample for each image. */
194    private long[] imageOffsets;
195
196    /** The image sizes. */
197    private Dimension[] imageDimensions;
198
199    /** Constructor.
200     *  @param source The <code>ImageInputStream</code> containing all the raw
201     *                images.
202     *  @param type   The <code>ImageTypeSpecifier</code> for all the images
203     *                in the stream.
204     *  @param imageOffsets The position of the first sample for each image
205     *                in the stream.
206     *  @param imageDimensions The image size for each image in the stream.
207     *
208     *  @throws IllegalArgumentException If the sizes of
209     *                <code>imageOffsets</code>
210     *                and <code>imageDimensions</code> are different or if
211     *                either array is null.
212     */
213    public RawImageInputStream(ImageInputStream source,
214                               ImageTypeSpecifier type,
215                               long[] imageOffsets,
216                               Dimension[] imageDimensions) {
217        if (imageOffsets == null || imageDimensions == null ||
218            imageOffsets.length != imageDimensions.length) {
219            throw new IllegalArgumentException
220                (I18N.getString("RawImageInputStream0"));
221        }
222
223        this.source = source;
224        this.type = type;
225        this.imageOffsets = imageOffsets;
226        this.imageDimensions = imageDimensions;
227    }
228
229    /**
230     * Constructor.
231     *
232     * <p>This constructor is the same as
233     * {@link #RawImageInputStream(ImageInputStream,ImageTypeSpecifier,
234     * long[],Dimension[])} except that a <code>SampleModel</code> is
235     * supplied instead of an <code>ImageTypeSpecifier</code>. This
236     * constructor creates a <code>ColorModel</code> for the supplied
237     * <code>SampleModel</code> and then creates an
238     * <code>ImageTypeSpecifier</code>.</p>
239     *
240     * <p>Suitable <code>ColorModel</code>s are guaranteed to exist
241     * for all instances of <code>ComponentSampleModel</code>.
242     * For 1- and 3- banded <code>SampleModel</code>s, the
243     * <code>ColorModel</code> will be opaque.  For 2- and 4-banded
244     * <code>SampleModel</code>s, the output will use alpha transparency
245     * which is not premultiplied.  1- and 2-banded data will use a
246     * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB
247     * <code>ColorSpace</code>. Data with 5 or more bands will use a
248     * <code>ColorSpace</code> which satisfies compatibility constraints
249     * but is merely a placeholder and <i>does not perform correct color
250     * conversion to and from the C.I.E. XYZ and sRGB color spaces</i>.</p>
251     *
252     * <p>An instance of <code>DirectColorModel</code> will be created for
253     * instances of <code>SinglePixelPackedSampleModel</code> with no more
254     * than 4 bands.</p>
255     *
256     * <p>An instance of <code>IndexColorModel</code> will be created for
257     * instances of <code>MultiPixelPackedSampleModel</code>. The colormap
258     * will be a grayscale ramp with <code>1&nbsp;<<&nbsp;numberOfBits</code>
259     * entries ranging from zero to at most 255.</p>
260     *
261     *  @param source The <code>ImageInputStream</code> containing all the raw
262     *                images.
263     *  @param sampleModel The <code>SampleModel</code> for all the images
264     *                in the stream.
265     *  @param imageOffsets The position of the first sample for each image
266     *                in the stream.
267     *  @param imageDimensions The image size for each image in the stream.
268     *
269     *  @throws IllegalArgumentException If <code>sampleModel</code> is null.
270     *  @throws IllegalArgumentException If the sizes of
271     *                <code>imageOffsets</code>
272     *                and <code>imageDimensions</code> are different or if
273     *                either array is null.
274     *  @throws IllegalArgumentException If it is not possible to create a
275     *                <code>ColorModel</code> from the supplied
276     *                <code>SampleModel</code>.
277     */
278    public RawImageInputStream(ImageInputStream source,
279                               SampleModel sampleModel,
280                               long[] imageOffsets,
281                               Dimension[] imageDimensions) {
282        if (imageOffsets == null || imageDimensions == null ||
283            imageOffsets.length != imageDimensions.length) {
284            throw new IllegalArgumentException
285                (I18N.getString("RawImageInputStream0"));
286        }
287
288        this.source = source;
289        ColorModel colorModel = ImageUtil.createColorModel(sampleModel);
290        if(colorModel == null) {
291            throw new IllegalArgumentException
292                (I18N.getString("RawImageInputStream4"));
293        }
294        this.type = new ImageTypeSpecifier(colorModel, sampleModel);
295        this.imageOffsets = imageOffsets;
296        this.imageDimensions = imageDimensions;
297    }
298
299    /**
300     * Constructor.
301     *
302     * The <code>xmlSource</code> must adhere to the following DTD:
303     *
304     *<pre>
305     *&lt;!DOCTYPE "com_sun_media_imageio_stream_raw_1.0" [
306     *
307     *  &lt;!ELEMENT com_sun_media_imageio_stream_raw_1.0
308     *    (byteOrder?, offset?, width?, height?,
309     *     (ComponentSampleModel |
310     *      MultiPixelPackedSampleModel |
311     *      SinglePixelPackedSampleModel),
312     *     (ComponentColorModel |
313     *      DirectColorModel |
314     *      IndexColorModel)?)&gt;
315     *
316     *    &lt;!ATTLIST com_sun_media_imageio_stream_raw_1.0
317     *      xmlns CDATA #FIXED "http://com/sun/media/imageio"&gt;
318     *
319     *  &lt;!ELEMENT byteOrder (#PCDATA)&gt;
320     *    &lt;!-- Byte order of data stream --&gt;
321     *    &lt;!-- Either "NETWORK" or "REVERSE" --&gt;
322     *    &lt;!-- Data type: String --&gt;
323     *
324     *  &lt;!ELEMENT offset (#PCDATA)&gt;
325     *    &lt;!-- Byte offset to the image data in the stream --&gt;
326     *    &lt;!-- Data type: long --&gt;
327     *
328     *  &lt;!ELEMENT width (#PCDATA)&gt;
329     *    &lt;!-- Image width; default value is SampleModel width --&gt;
330     *    &lt;!-- Data type: int --&gt;
331     *
332     *  &lt;!ELEMENT height (#PCDATA)&gt;
333     *    &lt;!-- Image height; default value is SampleModel height --&gt;
334     *    &lt;!-- Data type: int --&gt;
335     *
336     *  &lt;!ELEMENT ComponentSampleModel EMPTY&gt;
337     *    &lt;!-- ComponentSampleModel --&gt;
338     *
339     *    &lt;!ATTLIST ComponentSampleModel
340     *      dataType (BYTE | USHORT | SHORT | INT | FLOAT | DOUBLE) #REQUIRED
341     *        &lt;!-- Data type: String --&gt;
342     *      w              CDATA #REQUIRED
343     *        &lt;!-- SampleModel width --&gt;
344     *        &lt;!-- Data type: int --&gt;
345     *      h              CDATA #REQUIRED
346     *        &lt;!-- SampleModel height --&gt;
347     *        &lt;!-- Data type: int --&gt;
348     *      pixelStride    CDATA "1"
349     *        &lt;!-- SampleModel pixel stride --&gt;
350     *        &lt;!-- Data type: int --&gt;
351     *      scanlineStride CDATA #REQUIRED
352     *        &lt;!-- SampleModel line stride --&gt;
353     *        &lt;!-- Data type: int --&gt;
354     *      bankIndices    CDATA #IMPLIED
355     *        &lt;!-- SampleModel bank indices --&gt;
356     *        &lt;!-- Data type: int array --&gt;
357     *      bandOffsets    CDATA #REQUIRED&gt;
358     *        &lt;!-- SampleModel band offsets --&gt;
359     *        &lt;!-- Data type: int array --&gt;
360     *
361     *  &lt;!ELEMENT MultiPixelPackedSampleModel EMPTY&gt;
362     *    &lt;!-- MultiPixelPackedSampleModel --&gt;
363     *
364     *    &lt;!ATTLIST MultiPixelPackedSampleModel
365     *      dataType       (BYTE | USHORT | INT) #REQUIRED
366     *        &lt;!-- Data type: String --&gt;
367     *      w              CDATA #REQUIRED
368     *        &lt;!-- SampleModel width --&gt;
369     *        &lt;!-- Data type: int --&gt;
370     *      h              CDATA #REQUIRED
371     *        &lt;!-- SampleModel height --&gt;
372     *        &lt;!-- Data type: int --&gt;
373     *      numberOfBits   CDATA #REQUIRED
374     *        &lt;!-- Number of bits per pixel --&gt;
375     *        &lt;!-- Data type: int --&gt;
376     *      scanlineStride CDATA #REQUIRED
377     *        &lt;!-- SampleModel line stride --&gt;
378     *        &lt;!-- Data type: int --&gt;
379     *      dataBitOffset  CDATA "0"&gt;
380     *        &lt;!-- Offset to first valid bit in a line --&gt;
381     *        &lt;!-- Data type: int --&gt;
382     *
383     *  &lt;!ELEMENT SinglePixelPackedSampleModel EMPTY&gt;
384     *    &lt;!-- SinglePixelPackedSampleModel --&gt;
385     *
386     *    &lt;!ATTLIST SinglePixelPackedSampleModel
387     *      dataType       (BYTE | USHORT | INT) #REQUIRED
388     *        &lt;!-- Data type: String --&gt;
389     *      w              CDATA #REQUIRED
390     *        &lt;!-- SampleModel width --&gt;
391     *        &lt;!-- Data type: int --&gt;
392     *      h              CDATA #REQUIRED
393     *        &lt;!-- SampleModel height --&gt;
394     *        &lt;!-- Data type: int --&gt;
395     *      scanlineStride CDATA #REQUIRED
396     *        &lt;!-- SampleModel line stride --&gt;
397     *        &lt;!-- Data type: int --&gt;
398     *      bitMasks       CDATA #REQUIRED&gt;
399     *        &lt;!-- Masks indicating RGBA positions --&gt;
400     *        &lt;!-- Data type: int --&gt;
401     *
402     *  &lt;!ELEMENT ComponentColorModel EMPTY&gt;
403     *    &lt;!-- ComponentColorModel --&gt;
404     *
405     *    &lt;!ATTLIST ComponentColorModel
406     *      colorSpace (CIEXYZ | GRAY | LINEAR_RGB | PYCC | sRGB | URL)
407     *                     #REQUIRED
408     *        &lt;!-- A string representing a predefined ColorSpace or a URI
409     *                representing the location of any ICC profile from which
410     *                a ColorSpace may be created. --&gt;
411     *        &lt;!-- Data type: String --&gt;
412     *      bits                 CDATA          #IMPLIED
413     *        &lt;!-- Number of bits per color component --&gt;
414     *        &lt;!-- Data type: int --&gt;
415     *      hasAlpha             (true | false) #REQUIRED
416     *        &lt;!-- Whether an alpha channel is present --&gt;
417     *        &lt;!-- Data type: boolean --&gt;
418     *      isAlphaPremultiplied (true | false) #REQUIRED
419     *        &lt;!-- Whether any alpha channel is premultiplied --&gt;
420     *        &lt;!-- Data type: boolean --&gt;
421     *      transparency         (BITMASK | OPAQUE | TRANSLUCENT) #REQUIRED
422     *        &lt;!-- The type of transparency --&gt;
423     *      transferType (BYTE | USHORT | SHORT | INT | FLOAT | DOUBLE)
424     *                     #REQUIRED&gt;
425     *        &lt;!-- The data transfer type --&gt;
426     *
427     *  &lt;!ELEMENT DirectColorModel EMPTY&gt;
428     *    &lt;!-- DirectColorModel --&gt;
429     *
430     *    &lt;!ATTLIST DirectColorModel
431     *      colorSpace           (LINEAR_RGB | sRGB | URL) #IMPLIED
432     *        &lt;!-- A string representing a predefined RGB ColorSpace or a
433     *                URL representing the location of any ICC profile from
434     *                which an RGB ColorSpace may be created. --&gt;
435     *        &lt;!-- Data type: String --&gt;
436     *      bits       CDATA #REQUIRED
437     *        &lt;!-- Number of bits per color component --&gt;
438     *        &lt;!-- Data type: int --&gt;
439     *      rmask      CDATA #REQUIRED
440     *        &lt;!-- Bitmask of the red component --&gt;
441     *        &lt;!-- Data type: int --&gt;
442     *      gmask      CDATA #REQUIRED
443     *        &lt;!-- Bitmask of the grenn component --&gt;
444     *        &lt;!-- Data type: int --&gt;
445     *      bmask      CDATA #REQUIRED
446     *        &lt;!-- Bitmask of the blue component --&gt;
447     *        &lt;!-- Data type: int --&gt;
448     *      amask      CDATA "0"&gt;
449     *        &lt;!-- Bitmask of the alpha component --&gt;
450     *        &lt;!-- Data type: int --&gt;
451     *
452     *  &lt;!ELEMENT IndexColorModel EMPTY&gt;
453     *    &lt;!-- IndexColorModel --&gt;
454     *
455     *    &lt;!ATTLIST IndexColorModel
456     *      bits CDATA #REQUIRED
457     *        &lt;!-- Number of bits per color component --&gt;
458     *        &lt;!-- Data type: int --&gt;
459     *      size CDATA #REQUIRED
460     *        &lt;!-- Number of elements in the colormap --&gt;
461     *        &lt;!-- Data type: int --&gt;
462     *      r    CDATA #REQUIRED
463     *        &lt;!-- Red elements of the colormap --&gt;
464     *        &lt;!-- Data type: byte array --&gt;
465     *      g    CDATA #REQUIRED
466     *        &lt;!-- Green elements of the colormap --&gt;
467     *        &lt;!-- Data type: byte array --&gt;
468     *      b    CDATA #REQUIRED
469     *        &lt;!-- Blue elements of the colormap --&gt;
470     *        &lt;!-- Data type: byte array --&gt;
471     *      a    CDATA #IMPLIED&gt;
472     *        &lt;!-- Alpha elements of the colormap --&gt;
473     *        &lt;!-- Data type: byte array --&gt;
474     *]&gt;
475     *</pre>
476     *
477     *
478     *  @param source The <code>ImageInputStream</code> containing all the raw
479     *                images.
480     *  @param xmlSource The <code>org.xml.sax.InputSource</code> to provide
481     *                the xml document in which the stream structure is defined.
482     *
483     *  @throws RuntimeException If the parse configuration isn't correct.
484     *
485     *  @throws IllegalArgumentException If the number of "width" elements isn't
486     *                the same as the number of "height" elements.
487     *
488     *  @throws SAXException If one is thrown in parsing.
489     *
490     *  @throws IOException If one is thrown in parsing, or creating color space
491     *                from a URL.
492     */
493    public RawImageInputStream(ImageInputStream source,
494                               org.xml.sax.InputSource xmlSource)
495                               throws SAXException, IOException {
496        this.source = source;
497
498        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
499        dbf.setValidating(true);
500        dbf.setNamespaceAware(true);
501        dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
502                       "http://www.w3.org/2001/XMLSchema");
503        DocumentBuilder db = null;
504        try {
505            db = dbf.newDocumentBuilder();
506        } catch (ParserConfigurationException ex) {
507            throw new RuntimeException(I18N.getString("RawImageInputStream1"),
508                                                      ex);
509        }
510
511        Document doc = db.parse(xmlSource);
512
513        //gets the byte order
514        NodeList nodes = doc.getElementsByTagName("byteOrder");
515        String byteOrder = nodes.item(0).getNodeValue();
516        if ("NETWORK".equals(byteOrder)) {
517            this.setByteOrder(ByteOrder.BIG_ENDIAN);
518            this.source.setByteOrder(ByteOrder.BIG_ENDIAN);
519        } else  if ("REVERSE".equals(byteOrder)) {
520            this.setByteOrder(ByteOrder.LITTLE_ENDIAN);
521            this.setByteOrder(ByteOrder.LITTLE_ENDIAN);
522        }
523
524        //gets image offsets
525        nodes = doc.getElementsByTagName("offset");
526        int length = nodes.getLength();
527        this.imageOffsets = new long[length];
528        for (int i = 0; i < length; i++) {
529            imageOffsets[i] = new Long(nodes.item(i).getNodeValue()).longValue();
530        }
531
532        //gets image dimensions
533        nodes = doc.getElementsByTagName("width");
534        NodeList nodes1 = doc.getElementsByTagName("height");
535        length = nodes.getLength();
536        if (length != nodes1.getLength())
537            throw new IllegalArgumentException
538                (I18N.getString("RawImageInputStream2"));
539
540        this.imageDimensions = new Dimension[length];
541        for (int i = 0; i < length; i++) {
542            String w = nodes.item(i).getNodeValue();
543            String h = nodes1.item(i).getNodeValue();
544
545            imageDimensions[i] =
546                new Dimension((new Integer(w)).intValue(),
547                              (new Integer(h)).intValue());
548        }
549
550        //get sampleModel
551        SampleModel sampleModel = null;
552
553        // for ComponentSampleModel
554        nodes = doc.getElementsByTagName("ComponentSampleModel");
555        if (nodes.getLength() > 0) {
556            Node node = nodes.item(0);
557            int[] bankIndices =getIntArray (node, "bankIndices");
558
559            if (bankIndices == null)
560                sampleModel =
561                    new ComponentSampleModel(getInt(node, "dataType"),
562                                             getInt(node, "w"),
563                                             getInt(node, "h"),
564                                             getInt(node, "pixelStride"),
565                                             getInt(node, "scanlineStride"),
566                                             getIntArray(node, "bandOffsets"));
567            else
568                sampleModel =
569                    new ComponentSampleModel(getInt(node, "dataType"),
570                                             getInt(node, "w"),
571                                             getInt(node, "h"),
572                                             getInt(node, "pixelStride"),
573                                             getInt(node, "scanlineStride"),
574                                             bankIndices,
575                                             getIntArray(node, "bandOffsets"));
576        }
577
578        // for MultiPixelPackedSampleModel
579        nodes = doc.getElementsByTagName("MultiPixelPackedSampleModel");
580        if (nodes.getLength() > 0) {
581            Node node = nodes.item(0);
582            sampleModel =
583                new MultiPixelPackedSampleModel(getInt(node, "dataType"),
584                                         getInt(node, "w"),
585                                         getInt(node, "h"),
586                                         getInt(node, "numberOfBits"),
587                                         getInt(node, "scanlineStride"),
588                                         getInt(node, "dataBitOffset"));
589        }
590
591        // for SinglePixelPackedSampleModel
592        nodes = doc.getElementsByTagName("SinglePixelPackedSampleModel");
593        if (nodes.getLength() > 0) {
594            Node node = nodes.item(0);
595            sampleModel =
596                new SinglePixelPackedSampleModel(getInt(node, "dataType"),
597                                         getInt(node, "w"),
598                                         getInt(node, "h"),
599                                         getInt(node, "scanlineStride"),
600                                         getIntArray(node, "bitMasks"));
601        }
602
603        //get colorModel
604        ColorModel colorModel = null;
605
606        // for ComponentColorModel
607        nodes = doc.getElementsByTagName("ComponentColorModel");
608        if (nodes.getLength() > 0) {
609            Node node = nodes.item(0);
610            colorModel =
611                new ComponentColorModel(getColorSpace(node),
612                                        getIntArray(node, "bits"),
613                                        getBoolean(node, "hasAlpha"),
614                                        getBoolean(node, "isAlphaPremultiplied"),
615                                        getTransparency(getAttribute(node, "transparency")),
616                                        getInt(node, "transferType"));
617        }
618
619        // for DirectColorModel
620        nodes = doc.getElementsByTagName("DirectColorModel");
621        if (nodes.getLength() > 0) {
622            Node node = nodes.item(0);
623            colorModel =
624                new DirectColorModel(getColorSpace(node),
625                                     getInt(node, "bits"),
626                                     getInt(node, "rmask"),
627                                     getInt(node, "gmask"),
628                                     getInt(node, "bmask"),
629                                     getInt(node, "amask"),
630                                     false,
631                                     Transparency.OPAQUE);
632        }
633
634        // for IndexColorModel
635        nodes = doc.getElementsByTagName("IndexColorModel");
636        if (nodes.getLength() > 0) {
637            Node node = nodes.item(0);
638            byte[] alpha = getByteArray(node, "a");
639
640            if (alpha == null)
641                colorModel =
642                    new IndexColorModel(getInt(node, "bits"),
643                                        getInt(node, "size"),
644                                        getByteArray(node, "r"),
645                                        getByteArray(node, "g"),
646                                        getByteArray(node, "b"));
647            else
648                colorModel =
649                    new IndexColorModel(getInt(node, "bits"),
650                                        getInt(node, "size"),
651                                        getByteArray(node, "r"),
652                                        getByteArray(node, "g"),
653                                        getByteArray(node, "b"),
654                                        alpha);
655        }
656
657        //create image type
658        this.type = new ImageTypeSpecifier(colorModel, sampleModel);
659
660        //assign imagedimension based on the sample model
661        if (this.imageDimensions.length == 0) {
662            this.imageDimensions = new Dimension[this.imageOffsets.length];
663
664            imageDimensions[0] = new Dimension(sampleModel.getWidth(),
665                                               sampleModel.getHeight());
666            for (int i = 1; i < imageDimensions.length; i++)
667                imageDimensions[i] = imageDimensions[0];
668        }
669    }
670
671    /**
672     * Retrieves the image type.
673     *
674     * @return the image type
675     */
676    public ImageTypeSpecifier getImageType() {
677        return type;
678    }
679
680    /**
681     * Retrieves the image offset of the <code>imageIndex</code>th image.
682     *
683     * @param imageIndex the index of the image of interest.
684     * @throws IllegalArgumentException If the provided parameter is out of
685     *              range.
686     * @return the offset in the stream to the specified image.
687     */
688    public long getImageOffset(int imageIndex) {
689        if (imageIndex < 0 || imageIndex >= imageOffsets.length)
690            throw new IllegalArgumentException
691                (I18N.getString("RawImageInputStream3"));
692        return imageOffsets[imageIndex];
693    }
694
695    /** Retrieves the dimnsion of the <code>imageIndex</code>th image.
696     * @param imageIndex the index of the image of interest.
697     *  @throws IllegalArgumentException If the provided parameter is out of
698     *              rangle.
699     * @return the size of the specified image.
700     */
701    public Dimension getImageDimension(int imageIndex) {
702        if (imageIndex < 0 || imageIndex >= imageOffsets.length)
703            throw new IllegalArgumentException
704                (I18N.getString("RawImageInputStream3"));
705        return imageDimensions[imageIndex];
706    }
707
708    /**
709     * Retrieves the number of images in the <code>ImageInputStream</code>.
710     * @return the number of image in the stream.
711     */
712    public int getNumImages() {
713        return imageOffsets.length;
714    }
715
716    public void setByteOrder(ByteOrder byteOrder) {
717        source.setByteOrder(byteOrder);
718    }
719
720    public ByteOrder getByteOrder() {
721        return source.getByteOrder();
722    }
723
724    public int read() throws IOException {
725        return source.read();
726    }
727
728    public int read(byte[] b) throws IOException {
729        return source.read(b);
730    }
731
732    public int read(byte[] b, int off, int len) throws IOException {
733        return source.read(b, off, len);
734    }
735
736    public void readBytes(IIOByteBuffer buf, int len) throws IOException {
737        source.readBytes(buf, len);
738    }
739
740    public boolean readBoolean() throws IOException {
741        return source.readBoolean();
742    }
743
744    public byte readByte() throws IOException {
745        return source.readByte();
746    }
747
748    public int readUnsignedByte() throws IOException {
749        return source.readUnsignedByte();
750    }
751
752    public short readShort() throws IOException {
753        return source.readShort();
754    }
755
756    public int readUnsignedShort() throws IOException {
757        return source.readUnsignedShort();
758    }
759
760    public char readChar() throws IOException {
761        return source.readChar();
762    }
763
764    public int readInt() throws IOException {
765        return source.readInt();
766    }
767
768    public long readUnsignedInt() throws IOException {
769        return source.readUnsignedInt();
770    }
771
772    public long readLong() throws IOException {
773        return source.readLong();
774    }
775
776    public float readFloat() throws IOException {
777        return source.readFloat();
778    }
779
780    public double readDouble() throws IOException {
781        return source.readDouble();
782    }
783
784    public String readLine() throws IOException {
785        return source.readLine();
786    }
787
788    public String readUTF() throws IOException {
789        return source.readUTF();
790    }
791
792    public void readFully(byte[] b, int off, int len) throws IOException {
793        source.readFully(b, off, len);
794    }
795
796    public void readFully(byte[] b) throws IOException {
797        source.readFully(b);
798    }
799
800    public void readFully(short[] s, int off, int len) throws IOException {
801        source.readFully(s, off, len);
802    }
803
804    public void readFully(char[] c, int off, int len) throws IOException {
805        source.readFully(c, off, len);
806    }
807
808    public void readFully(int[] i, int off, int len) throws IOException {
809        source.readFully(i, off, len);
810    }
811
812    public void readFully(long[] l, int off, int len) throws IOException {
813        source.readFully(l, off, len);
814    }
815
816    public void readFully(float[] f, int off, int len) throws IOException {
817        source.readFully(f, off, len);
818    }
819
820    public void readFully(double[] d, int off, int len) throws IOException {
821        source.readFully(d, off, len);
822    }
823
824    public long getStreamPosition() throws IOException {
825        return source.getStreamPosition();
826    }
827
828    public int getBitOffset() throws IOException {
829        return source.getBitOffset();
830    }
831
832    public void setBitOffset(int bitOffset) throws IOException {
833        source.setBitOffset(bitOffset);
834    }
835
836    public int readBit() throws IOException {
837        return source.readBit();
838    }
839
840    public long readBits(int numBits) throws IOException {
841        return source.readBits(numBits);
842    }
843
844    public long length() throws IOException {
845        return source.length();
846    }
847
848    public int skipBytes(int n) throws IOException {
849        return source.skipBytes(n);
850    }
851
852    public long skipBytes(long n) throws IOException {
853        return source.skipBytes(n);
854    }
855
856    public void seek(long pos) throws IOException {
857        source.seek(pos);
858    }
859
860    public void mark() {
861        source.mark();
862    }
863
864    public void reset() throws IOException {
865        source.reset();
866    }
867
868    public void flushBefore(long pos) throws IOException {
869        source.flushBefore(pos);
870    }
871
872    public void flush() throws IOException {
873        source.flush();
874    }
875
876    public long getFlushedPosition() {
877        return source.getFlushedPosition();
878    }
879
880    public boolean isCached() {
881        return source.isCached();
882    }
883
884    public boolean isCachedMemory() {
885        return source.isCachedMemory();
886    }
887
888    public boolean isCachedFile() {
889        return source.isCachedFile();
890    }
891
892    public void close() throws IOException {
893        source.close();
894    }
895}