KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > imageio > plugins > png > PNGImageReader


1 /*
2  * @(#)PNGImageReader.java 1.56 06/04/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.imageio.plugins.png;
9
10 import java.awt.Point JavaDoc;
11 import java.awt.Rectangle JavaDoc;
12 import java.awt.color.ColorSpace JavaDoc;
13 import java.awt.image.BufferedImage JavaDoc;
14 import java.awt.image.DataBuffer JavaDoc;
15 import java.awt.image.DataBufferByte JavaDoc;
16 import java.awt.image.DataBufferUShort JavaDoc;
17 import java.awt.image.Raster JavaDoc;
18 import java.awt.image.WritableRaster JavaDoc;
19 import java.io.BufferedInputStream JavaDoc;
20 import java.io.ByteArrayInputStream JavaDoc;
21 import java.io.DataInputStream JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.SequenceInputStream JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.zip.Inflater JavaDoc;
31 import java.util.zip.InflaterInputStream JavaDoc;
32 import javax.imageio.IIOException JavaDoc;
33 import javax.imageio.ImageReader JavaDoc;
34 import javax.imageio.ImageReadParam JavaDoc;
35 import javax.imageio.ImageTypeSpecifier JavaDoc;
36 import javax.imageio.metadata.IIOMetadata JavaDoc;
37 import javax.imageio.spi.ImageReaderSpi JavaDoc;
38 import javax.imageio.stream.ImageInputStream JavaDoc;
39 import com.sun.imageio.plugins.common.InputStreamAdapter;
40 import com.sun.imageio.plugins.common.SubImageInputStream;
41
42 class PNGImageDataEnumeration implements Enumeration JavaDoc {
43
44     boolean firstTime = true;
45     ImageInputStream JavaDoc stream;
46     int length;
47     
48     public PNGImageDataEnumeration(ImageInputStream JavaDoc stream)
49         throws IOException JavaDoc {
50         this.stream = stream;
51         this.length = stream.readInt();
52         int type = stream.readInt(); // skip chunk type
53
}
54
55     public Object JavaDoc nextElement() {
56         try {
57             firstTime = false;
58             ImageInputStream JavaDoc iis = new SubImageInputStream(stream, length);
59             return new InputStreamAdapter(iis);
60         } catch (IOException JavaDoc e) {
61             return null;
62         }
63     }
64
65     public boolean hasMoreElements() {
66         if (firstTime) {
67             return true;
68         }
69
70         try {
71             int crc = stream.readInt();
72             this.length = stream.readInt();
73             int type = stream.readInt();
74             if (type == PNGImageReader.IDAT_TYPE) {
75                 return true;
76             } else {
77                 return false;
78             }
79         } catch (IOException JavaDoc e) {
80             return false;
81         }
82     }
83 }
84
85 /**
86  * @version 0.5
87  */

88 public class PNGImageReader extends ImageReader JavaDoc {
89
90     // Critical chunks
91
static final int IHDR_TYPE = chunkType("IHDR");
92     static final int PLTE_TYPE = chunkType("PLTE");
93     static final int IDAT_TYPE = chunkType("IDAT");
94     static final int IEND_TYPE = chunkType("IEND");
95     
96     // Ancillary chunks
97
static final int bKGD_TYPE = chunkType("bKGD");
98     static final int cHRM_TYPE = chunkType("cHRM");
99     static final int gAMA_TYPE = chunkType("gAMA");
100     static final int hIST_TYPE = chunkType("hIST");
101     static final int iCCP_TYPE = chunkType("iCCP");
102     static final int iTXt_TYPE = chunkType("iTXt");
103     static final int pHYs_TYPE = chunkType("pHYs");
104     static final int sBIT_TYPE = chunkType("sBIT");
105     static final int sPLT_TYPE = chunkType("sPLT");
106     static final int sRGB_TYPE = chunkType("sRGB");
107     static final int tEXt_TYPE = chunkType("tEXt");
108     static final int tIME_TYPE = chunkType("tIME");
109     static final int tRNS_TYPE = chunkType("tRNS");
110     static final int zTXt_TYPE = chunkType("zTXt");
111
112     static final int PNG_COLOR_GRAY = 0;
113     static final int PNG_COLOR_RGB = 2;
114     static final int PNG_COLOR_PALETTE = 3;
115     static final int PNG_COLOR_GRAY_ALPHA = 4;
116     static final int PNG_COLOR_RGB_ALPHA = 6;
117
118     // The number of bands by PNG color type
119
static final int[] inputBandsForColorType = {
120          1, // gray
121
-1, // unused
122
3, // rgb
123
1, // palette
124
2, // gray + alpha
125
-1, // unused
126
4 // rgb + alpha
127
};
128
129     static final int PNG_FILTER_NONE = 0;
130     static final int PNG_FILTER_SUB = 1;
131     static final int PNG_FILTER_UP = 2;
132     static final int PNG_FILTER_AVERAGE = 3;
133     static final int PNG_FILTER_PAETH = 4;
134
135     static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 };
136     static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 };
137     static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 };
138     static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 };
139
140     private static final boolean debug = true;
141
142     ImageInputStream JavaDoc stream = null;
143
144     boolean gotHeader = false;
145     boolean gotMetadata = false;
146
147     ImageReadParam JavaDoc lastParam = null;
148
149     long imageStartPosition = -1L;
150
151     Rectangle JavaDoc sourceRegion = null;
152     int sourceXSubsampling = -1;
153     int sourceYSubsampling = -1;
154     int sourceMinProgressivePass = 0;
155     int sourceMaxProgressivePass = 6;
156     int[] sourceBands = null;
157     int[] destinationBands = null;
158     Point JavaDoc destinationOffset = new Point JavaDoc(0, 0);
159
160     PNGMetadata metadata = new PNGMetadata();
161
162     DataInputStream JavaDoc pixelStream = null;
163
164     BufferedImage JavaDoc theImage = null;
165
166     // The number of source pixels processed
167
int pixelsDone = 0;
168
169     // The total number of pixels in the source image
170
int totalPixels;
171
172     public PNGImageReader(ImageReaderSpi JavaDoc originatingProvider) {
173         super(originatingProvider);
174     }
175
176     public void setInput(Object JavaDoc input,
177                          boolean seekForwardOnly,
178                          boolean ignoreMetadata) {
179         super.setInput(input, seekForwardOnly, ignoreMetadata);
180         this.stream = (ImageInputStream JavaDoc)input; // Always works
181

182         // Clear all values based on the previous stream contents
183
resetStreamSettings();
184     }
185
186     // Callable from ImageWriter
187
static int chunkType(String JavaDoc typeString) {
188         char c0 = typeString.charAt(0);
189         char c1 = typeString.charAt(1);
190         char c2 = typeString.charAt(2);
191         char c3 = typeString.charAt(3);
192
193         int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
194         return type;
195     }
196
197     private String JavaDoc readNullTerminatedString() throws IOException JavaDoc {
198         StringBuffer JavaDoc b = new StringBuffer JavaDoc();
199         int c;
200
201         while ((c = stream.read()) != 0) {
202             b.append((char)c);
203         }
204         return b.toString();
205     }
206
207     private void readHeader() throws IIOException JavaDoc {
208         if (gotHeader) {
209             return;
210         }
211         if (stream == null) {
212             throw new IllegalStateException JavaDoc("Input source not set!");
213         }
214
215         try {
216             byte[] signature = new byte[8];
217             stream.readFully(signature);
218             
219             if (signature[0] != (byte)137 ||
220                 signature[1] != (byte)80 ||
221                 signature[2] != (byte)78 ||
222                 signature[3] != (byte)71 ||
223                 signature[4] != (byte)13 ||
224                 signature[5] != (byte)10 ||
225                 signature[6] != (byte)26 ||
226                 signature[7] != (byte)10) {
227                 throw new IIOException JavaDoc("Bad PNG signature!");
228             }
229             
230             int IHDR_length = stream.readInt();
231             if (IHDR_length != 13) {
232                 throw new IIOException JavaDoc("Bad length for IHDR chunk!");
233             }
234             int IHDR_type = stream.readInt();
235             if (IHDR_type != IHDR_TYPE) {
236                 throw new IIOException JavaDoc("Bad type for IHDR chunk!");
237             }
238
239             this.metadata = new PNGMetadata();
240
241             int width = stream.readInt();
242             int height = stream.readInt();
243             int bitDepth = stream.readUnsignedByte();
244             int colorType = stream.readUnsignedByte();
245             int compressionMethod = stream.readUnsignedByte();
246             int filterMethod = stream.readUnsignedByte();
247             int interlaceMethod = stream.readUnsignedByte();
248             
249             int IHDR_CRC = stream.readInt();
250
251             stream.flushBefore(stream.getStreamPosition());
252
253             if (width == 0) {
254                 throw new IIOException JavaDoc("Image width == 0!");
255             }
256             if (height == 0) {
257                 throw new IIOException JavaDoc("Image height == 0!");
258             }
259             if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
260                 bitDepth != 8 && bitDepth != 16) {
261                 throw new IIOException JavaDoc("Bit depth must be 1, 2, 4, 8, or 16!");
262             }
263             if (colorType != 0 && colorType != 2 && colorType != 3 &&
264                 colorType != 4 && colorType != 6) {
265                 throw new IIOException JavaDoc("Color type must be 0, 2, 3, 4, or 6!");
266             }
267             if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
268                 throw new IIOException JavaDoc("Bad color type/bit depth combination!");
269             }
270             if ((colorType == PNG_COLOR_RGB ||
271                  colorType == PNG_COLOR_RGB_ALPHA ||
272                  colorType == PNG_COLOR_GRAY_ALPHA) &&
273                 (bitDepth != 8 && bitDepth != 16)) {
274                 throw new IIOException JavaDoc("Bad color type/bit depth combination!");
275             }
276             if (compressionMethod != 0) {
277                 throw new IIOException JavaDoc("Unknown compression method (not 0)!");
278             }
279             if (filterMethod != 0) {
280                 throw new IIOException JavaDoc("Unknown filter method (not 0)!");
281             }
282             if (interlaceMethod != 0 && interlaceMethod != 1) {
283                 throw new IIOException JavaDoc("Unknown interlace method (not 0 or 1)!");
284             }
285         
286             metadata.IHDR_present = true;
287             metadata.IHDR_width = width;
288             metadata.IHDR_height = height;
289             metadata.IHDR_bitDepth = bitDepth;
290             metadata.IHDR_colorType = colorType;
291             metadata.IHDR_compressionMethod = compressionMethod;
292             metadata.IHDR_filterMethod = filterMethod;
293             metadata.IHDR_interlaceMethod = interlaceMethod;
294             gotHeader = true;
295         } catch (IOException JavaDoc e) {
296             throw new IIOException JavaDoc("I/O error reading PNG header!", e);
297         }
298     }
299
300     private void parse_PLTE_chunk(int chunkLength) throws IOException JavaDoc {
301         if (metadata.PLTE_present) {
302             processWarningOccurred(
303 "A PNG image may not contain more than one PLTE chunk.\n" +
304 "The chunk wil be ignored.");
305             return;
306         } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
307                    metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
308             processWarningOccurred(
309 "A PNG gray or gray alpha image cannot have a PLTE chunk.\n" +
310 "The chunk wil be ignored.");
311             return;
312         }
313
314         byte[] palette = new byte[chunkLength];
315         stream.readFully(palette);
316
317         int numEntries = chunkLength/3;
318         if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
319             int maxEntries = 1 << metadata.IHDR_bitDepth;
320             if (numEntries > maxEntries) {
321                 processWarningOccurred(
322 "PLTE chunk contains too many entries for bit depth, ignoring extras.");
323                 numEntries = maxEntries;
324             }
325             numEntries = Math.min(numEntries, maxEntries);
326         }
327
328         // Round array sizes up to 2^2^n
329
int paletteEntries;
330         if (numEntries > 16) {
331             paletteEntries = 256;
332         } else if (numEntries > 4) {
333             paletteEntries = 16;
334         } else if (numEntries > 2) {
335             paletteEntries = 4;
336         } else {
337             paletteEntries = 2;
338         }
339
340         metadata.PLTE_present = true;
341         metadata.PLTE_red = new byte[paletteEntries];
342         metadata.PLTE_green = new byte[paletteEntries];
343         metadata.PLTE_blue = new byte[paletteEntries];
344
345         int index = 0;
346         for (int i = 0; i < numEntries; i++) {
347             metadata.PLTE_red[i] = palette[index++];
348             metadata.PLTE_green[i] = palette[index++];
349             metadata.PLTE_blue[i] = palette[index++];
350         }
351     }
352
353     private void parse_bKGD_chunk() throws IOException JavaDoc {
354         if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
355             metadata.bKGD_colorType = PNG_COLOR_PALETTE;
356             metadata.bKGD_index = stream.readUnsignedByte();
357         } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
358                    metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
359             metadata.bKGD_colorType = PNG_COLOR_GRAY;
360             metadata.bKGD_gray = stream.readUnsignedShort();
361         } else { // RGB or RGB_ALPHA
362
metadata.bKGD_colorType = PNG_COLOR_RGB;
363             metadata.bKGD_red = stream.readUnsignedShort();
364             metadata.bKGD_green = stream.readUnsignedShort();
365             metadata.bKGD_blue = stream.readUnsignedShort();
366         }
367
368         metadata.bKGD_present = true;
369     }
370
371     private void parse_cHRM_chunk() throws IOException JavaDoc {
372         metadata.cHRM_whitePointX = stream.readInt();
373         metadata.cHRM_whitePointY = stream.readInt();
374         metadata.cHRM_redX = stream.readInt();
375         metadata.cHRM_redY = stream.readInt();
376         metadata.cHRM_greenX = stream.readInt();
377         metadata.cHRM_greenY = stream.readInt();
378         metadata.cHRM_blueX = stream.readInt();
379         metadata.cHRM_blueY = stream.readInt();
380
381         metadata.cHRM_present = true;
382     }
383
384     private void parse_gAMA_chunk() throws IOException JavaDoc {
385         int gamma = stream.readInt();
386         metadata.gAMA_gamma = gamma;
387
388         metadata.gAMA_present = true;
389     }
390
391     private void parse_hIST_chunk() throws IOException JavaDoc, IIOException JavaDoc {
392         if (!metadata.PLTE_present) {
393             throw new IIOException JavaDoc("hIST chunk without prior PLTE chunk!");
394         }
395
396         metadata.hIST_histogram = new char[metadata.PLTE_red.length];
397         stream.readFully(metadata.hIST_histogram,
398                          0, metadata.hIST_histogram.length);
399
400         metadata.hIST_present = true;
401     }
402
403     private void parse_iCCP_chunk(int chunkLength) throws IOException JavaDoc {
404         String JavaDoc keyword = readNullTerminatedString();
405         metadata.iCCP_profileName = keyword;
406
407         metadata.iCCP_compressionMethod = stream.readUnsignedByte();
408
409         byte[] compressedProfile =
410           new byte[chunkLength - keyword.length() - 2];
411         stream.readFully(compressedProfile);
412         metadata.iCCP_compressedProfile = compressedProfile;
413
414         metadata.iCCP_present = true;
415     }
416   
417     private void parse_iTXt_chunk(int chunkLength) throws IOException JavaDoc {
418         long chunkStart = stream.getStreamPosition();
419
420         String JavaDoc keyword = readNullTerminatedString();
421         metadata.iTXt_keyword.add(keyword);
422         
423         int compressionFlag = stream.readUnsignedByte();
424         metadata.iTXt_compressionFlag.add(new Integer JavaDoc(compressionFlag));
425         
426         int compressionMethod = stream.readUnsignedByte();
427         metadata.iTXt_compressionMethod.add(new Integer JavaDoc(compressionMethod));
428         
429         String JavaDoc languageTag = readNullTerminatedString();
430         metadata.iTXt_languageTag.add(languageTag);
431         
432         String JavaDoc translatedKeyword = stream.readUTF();
433         metadata.iTXt_translatedKeyword.add(translatedKeyword);
434         stream.skipBytes(1); // Null separator
435

436         String JavaDoc text;
437         if (compressionFlag == 1) { // Decompress the text
438
long pos = stream.getStreamPosition();
439             byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
440             stream.readFully(b);
441             text = inflate(b);
442         } else {
443             text = stream.readUTF();
444         }
445         metadata.iTXt_text.add(text);
446     }
447
448     private void parse_pHYs_chunk() throws IOException JavaDoc {
449         metadata.pHYs_pixelsPerUnitXAxis = stream.readInt();
450         metadata.pHYs_pixelsPerUnitYAxis = stream.readInt();
451         metadata.pHYs_unitSpecifier = stream.readUnsignedByte();
452
453         metadata.pHYs_present = true;
454     }
455     
456     private void parse_sBIT_chunk() throws IOException JavaDoc {
457         int colorType = metadata.IHDR_colorType;
458         if (colorType == PNG_COLOR_GRAY ||
459             colorType == PNG_COLOR_GRAY_ALPHA) {
460             metadata.sBIT_grayBits = stream.readUnsignedByte();
461         } else if (colorType == PNG_COLOR_RGB ||
462                    colorType == PNG_COLOR_PALETTE ||
463                    colorType == PNG_COLOR_RGB_ALPHA) {
464             metadata.sBIT_redBits = stream.readUnsignedByte();
465             metadata.sBIT_greenBits = stream.readUnsignedByte();
466             metadata.sBIT_blueBits = stream.readUnsignedByte();
467         }
468
469         if (colorType == PNG_COLOR_GRAY_ALPHA ||
470             colorType == PNG_COLOR_RGB_ALPHA) {
471             metadata.sBIT_alphaBits = stream.readUnsignedByte();
472         }
473
474         metadata.sBIT_colorType = colorType;
475         metadata.sBIT_present = true;
476     }
477
478     private void parse_sPLT_chunk(int chunkLength)
479         throws IOException JavaDoc, IIOException JavaDoc {
480         metadata.sPLT_paletteName = readNullTerminatedString();
481         chunkLength -= metadata.sPLT_paletteName.length() + 1;
482
483         int sampleDepth = stream.readUnsignedByte();
484         metadata.sPLT_sampleDepth = sampleDepth;
485
486         int numEntries = chunkLength/(4*(sampleDepth/8) + 2);
487         metadata.sPLT_red = new int[numEntries];
488         metadata.sPLT_green = new int[numEntries];
489         metadata.sPLT_blue = new int[numEntries];
490         metadata.sPLT_alpha = new int[numEntries];
491         metadata.sPLT_frequency = new int[numEntries];
492
493         if (sampleDepth == 8) {
494             for (int i = 0; i < numEntries; i++) {
495                 metadata.sPLT_red[i] = stream.readUnsignedByte();
496                 metadata.sPLT_green[i] = stream.readUnsignedByte();
497                 metadata.sPLT_blue[i] = stream.readUnsignedByte();
498                 metadata.sPLT_alpha[i] = stream.readUnsignedByte();
499                 metadata.sPLT_frequency[i] = stream.readUnsignedShort();
500             }
501         } else if (sampleDepth == 16) {
502             for (int i = 0; i < numEntries; i++) {
503                 metadata.sPLT_red[i] = stream.readUnsignedShort();
504                 metadata.sPLT_green[i] = stream.readUnsignedShort();
505                 metadata.sPLT_blue[i] = stream.readUnsignedShort();
506                 metadata.sPLT_alpha[i] = stream.readUnsignedShort();
507                 metadata.sPLT_frequency[i] = stream.readUnsignedShort();
508             }
509         } else {
510             throw new IIOException JavaDoc("sPLT sample depth not 8 or 16!");
511         }
512
513         metadata.sPLT_present = true;
514     }
515
516     private void parse_sRGB_chunk() throws IOException JavaDoc {
517         metadata.sRGB_renderingIntent = stream.readUnsignedByte();
518
519         metadata.sRGB_present = true;
520     }
521
522     private void parse_tEXt_chunk(int chunkLength) throws IOException JavaDoc {
523         String JavaDoc keyword = readNullTerminatedString();
524         metadata.tEXt_keyword.add(keyword);
525
526         byte[] b = new byte[chunkLength - keyword.length() - 1];
527         stream.readFully(b);
528         metadata.tEXt_text.add(new String JavaDoc(b));
529     }
530
531     private void parse_tIME_chunk() throws IOException JavaDoc {
532         metadata.tIME_year = stream.readUnsignedShort();
533         metadata.tIME_month = stream.readUnsignedByte();
534         metadata.tIME_day = stream.readUnsignedByte();
535         metadata.tIME_hour = stream.readUnsignedByte();
536         metadata.tIME_minute = stream.readUnsignedByte();
537         metadata.tIME_second = stream.readUnsignedByte();
538
539         metadata.tIME_present = true;
540     }
541
542     private void parse_tRNS_chunk(int chunkLength) throws IOException JavaDoc {
543         int colorType = metadata.IHDR_colorType;
544         if (colorType == PNG_COLOR_PALETTE) {
545             if (!metadata.PLTE_present) {
546                 processWarningOccurred(
547 "tRNS chunk without prior PLTE chunk, ignoring it.");
548                 return;
549             }
550
551             // Alpha table may have fewer entries than RGB palette
552
int maxEntries = metadata.PLTE_red.length;
553             int numEntries = chunkLength;
554             if (numEntries > maxEntries) {
555                 processWarningOccurred(
556 "tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
557                 numEntries = maxEntries;
558             }
559             metadata.tRNS_alpha = new byte[numEntries];
560             metadata.tRNS_colorType = PNG_COLOR_PALETTE;
561             stream.read(metadata.tRNS_alpha, 0, numEntries);
562             stream.skipBytes(chunkLength - numEntries);
563         } else if (colorType == PNG_COLOR_GRAY) {
564             if (chunkLength != 2) {
565                 processWarningOccurred(
566 "tRNS chunk for gray image must have length 2, ignoring chunk.");
567                 stream.skipBytes(chunkLength);
568                 return;
569             }
570             metadata.tRNS_gray = stream.readUnsignedShort();
571             metadata.tRNS_colorType = PNG_COLOR_GRAY;
572         } else if (colorType == PNG_COLOR_RGB) {
573             if (chunkLength != 6) {
574                 processWarningOccurred(
575 "tRNS chunk for RGB image must have length 6, ignoring chunk.");
576                 stream.skipBytes(chunkLength);
577                 return;
578             }
579             metadata.tRNS_red = stream.readUnsignedShort();
580             metadata.tRNS_green = stream.readUnsignedShort();
581             metadata.tRNS_blue = stream.readUnsignedShort();
582             metadata.tRNS_colorType = PNG_COLOR_RGB;
583         } else {
584             processWarningOccurred(
585 "Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it.");
586             return;
587         }
588
589         metadata.tRNS_present = true;
590     }
591
592     private static String JavaDoc inflate(byte[] b) throws IOException JavaDoc {
593         InputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(b);
594         InputStream JavaDoc iis = new InflaterInputStream JavaDoc(bais);
595         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(80);
596         int c;
597         while ((c = iis.read()) != -1) {
598             sb.append((char)c);
599         }
600         return sb.toString();
601     }
602     
603     private void parse_zTXt_chunk(int chunkLength) throws IOException JavaDoc {
604         String JavaDoc keyword = readNullTerminatedString();
605         metadata.zTXt_keyword.add(keyword);
606
607         int method = stream.readUnsignedByte();
608         metadata.zTXt_compressionMethod.add(new Integer JavaDoc(method));
609
610         byte[] b = new byte[chunkLength - keyword.length() - 2];
611         stream.readFully(b);
612         metadata.zTXt_text.add(inflate(b));
613     }
614
615     private void readMetadata() throws IIOException JavaDoc {
616         if (gotMetadata) {
617             return;
618         }
619         
620         readHeader();
621
622         try {
623             while (true) {
624                 int chunkLength = stream.readInt();
625                 int chunkType = stream.readInt();
626
627                 // If chunk type is 'IDAT', we've reached the image data.
628
if (chunkType == IDAT_TYPE) {
629                     stream.skipBytes(-8);
630                     imageStartPosition = stream.getStreamPosition();
631                     break;
632                 }
633                 
634                 if (chunkType == PLTE_TYPE) {
635                     parse_PLTE_chunk(chunkLength);
636                 } else if (chunkType == bKGD_TYPE) {
637                     parse_bKGD_chunk();
638                 } else if (chunkType == cHRM_TYPE) {
639                     parse_cHRM_chunk();
640                 } else if (chunkType == gAMA_TYPE) {
641                     parse_gAMA_chunk();
642                 } else if (chunkType == hIST_TYPE) {
643                     parse_hIST_chunk();
644                 } else if (chunkType == iCCP_TYPE) {
645                     parse_iCCP_chunk(chunkLength);
646                 } else if (chunkType == iTXt_TYPE) {
647                     parse_iTXt_chunk(chunkLength);
648                 } else if (chunkType == pHYs_TYPE) {
649                     parse_pHYs_chunk();
650                 } else if (chunkType == sBIT_TYPE) {
651                     parse_sBIT_chunk();
652                 } else if (chunkType == sPLT_TYPE) {
653                     parse_sPLT_chunk(chunkLength);
654                 } else if (chunkType == sRGB_TYPE) {
655                     parse_sRGB_chunk();
656                 } else if (chunkType == tEXt_TYPE) {
657                     parse_tEXt_chunk(chunkLength);
658                 } else if (chunkType == tIME_TYPE) {
659                     parse_tIME_chunk();
660                 } else if (chunkType == tRNS_TYPE) {
661                     parse_tRNS_chunk(chunkLength);
662                 } else if (chunkType == zTXt_TYPE) {
663                     parse_zTXt_chunk(chunkLength);
664                 } else {
665                     // Read an unknown chunk
666
byte[] b = new byte[chunkLength];
667                     stream.readFully(b);
668
669                     StringBuffer JavaDoc chunkName = new StringBuffer JavaDoc(4);
670                     chunkName.append((char)(chunkType >>> 24));
671                     chunkName.append((char)((chunkType >> 16) & 0xff));
672                     chunkName.append((char)((chunkType >> 8) & 0xff));
673                     chunkName.append((char)(chunkType & 0xff));
674
675                     int ancillaryBit = chunkType >>> 28;
676                     if (ancillaryBit == 0) {
677                         processWarningOccurred(
678 "Encountered unknown chunk with critical bit set!");
679                     }
680
681                     metadata.unknownChunkType.add(chunkName.toString());
682                     metadata.unknownChunkData.add(b);
683                 }
684
685                 int chunkCRC = stream.readInt();
686                 stream.flushBefore(stream.getStreamPosition());
687             }
688         } catch (IOException JavaDoc e) {
689             e.printStackTrace();
690             throw new IIOException JavaDoc("Error reading PNG metadata", e);
691         }
692
693         gotMetadata = true;
694     }
695
696     // Data filtering methods
697

698     private static void decodeSubFilter(byte[] curr, int coff, int count,
699                                         int bpp) {
700         for (int i = bpp; i < count; i++) {
701             int val;
702
703             val = curr[i + coff] & 0xff;
704             val += curr[i + coff - bpp] & 0xff;
705
706             curr[i + coff] = (byte)val;
707         }
708     }
709
710     private static void decodeUpFilter(byte[] curr, int coff,
711                                        byte[] prev, int poff,
712                                        int count) {
713         for (int i = 0; i < count; i++) {
714             int raw = curr[i + coff] & 0xff;
715             int prior = prev[i + poff] & 0xff;
716
717             curr[i + coff] = (byte)(raw + prior);
718         }
719     }
720
721     private static void decodeAverageFilter(byte[] curr, int coff,
722                                             byte[] prev, int poff,
723                                             int count, int bpp) {
724         int raw, priorPixel, priorRow;
725
726         for (int i = 0; i < bpp; i++) {
727             raw = curr[i + coff] & 0xff;
728             priorRow = prev[i + poff] & 0xff;
729             
730             curr[i + coff] = (byte)(raw + priorRow/2);
731         }
732
733         for (int i = bpp; i < count; i++) {
734             raw = curr[i + coff] & 0xff;
735             priorPixel = curr[i + coff - bpp] & 0xff;
736             priorRow = prev[i + poff] & 0xff;
737             
738             curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2);
739         }
740     }
741
742     private static int paethPredictor(int a, int b, int c) {
743         int p = a + b - c;
744         int pa = Math.abs(p - a);
745         int pb = Math.abs(p - b);
746         int pc = Math.abs(p - c);
747
748         if ((pa <= pb) && (pa <= pc)) {
749             return a;
750         } else if (pb <= pc) {
751             return b;
752         } else {
753             return c;
754         }
755     }
756
757     private static void decodePaethFilter(byte[] curr, int coff,
758                                           byte[] prev, int poff,
759                                           int count, int bpp) {
760         int raw, priorPixel, priorRow, priorRowPixel;
761
762         for (int i = 0; i < bpp; i++) {
763             raw = curr[i + coff] & 0xff;
764             priorRow = prev[i + poff] & 0xff;
765
766             curr[i + coff] = (byte)(raw + priorRow);
767         }
768
769         for (int i = bpp; i < count; i++) {
770             raw = curr[i + coff] & 0xff;
771             priorPixel = curr[i + coff - bpp] & 0xff;
772             priorRow = prev[i + poff] & 0xff;
773             priorRowPixel = prev[i + poff - bpp] & 0xff;
774
775             curr[i + coff] = (byte)(raw + paethPredictor(priorPixel,
776                                                          priorRow,
777                                                          priorRowPixel));
778         }
779     }
780
781     private static final int[][] bandOffsets = {
782         null,
783         { 0 }, // G
784
{ 0, 1 }, // GA in GA order
785
{ 0, 1, 2 }, // RGB in RGB order
786
{ 0, 1, 2, 3 } // RGBA in RGBA order
787
};
788
789     private WritableRaster JavaDoc createRaster(int width, int height, int bands,
790                                         int scanlineStride,
791                                         int bitDepth) {
792
793         DataBuffer JavaDoc dataBuffer;
794         WritableRaster JavaDoc ras = null;
795         Point JavaDoc origin = new Point JavaDoc(0, 0);
796         if ((bitDepth < 8) && (bands == 1)) {
797             dataBuffer = new DataBufferByte JavaDoc(height*scanlineStride);
798             ras = Raster.createPackedRaster(dataBuffer,
799                                             width, height,
800                                             bitDepth,
801                                             origin);
802         } else if (bitDepth <= 8) {
803             dataBuffer = new DataBufferByte JavaDoc(height*scanlineStride);
804             ras = Raster.createInterleavedRaster(dataBuffer,
805                                                  width, height,
806                                                  scanlineStride,
807                                                  bands,
808                                                  bandOffsets[bands],
809                                                  origin);
810         } else {
811             dataBuffer = new DataBufferUShort JavaDoc(height*scanlineStride);
812             ras = Raster.createInterleavedRaster(dataBuffer,
813                                                  width, height,
814                                                  scanlineStride,
815                                                  bands,
816                                                  bandOffsets[bands],
817                                                  origin);
818         }
819
820         return ras;
821     }
822
823     private void skipPass(int passWidth, int passHeight)
824         throws IOException JavaDoc, IIOException JavaDoc {
825         if ((passWidth == 0) || (passHeight == 0)) {
826             return;
827         }
828
829         int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
830         int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8;
831         byte[] curr = new byte[bytesPerRow];
832
833         // Read the image row-by-row
834
for (int srcY = 0; srcY < passHeight; srcY++) {
835             // Read the filter type byte and a row of data
836
int filter = pixelStream.read();
837             pixelStream.readFully(curr, 0, bytesPerRow);
838
839             // If read has been aborted, just return
840
// processReadAborted will be called later
841
if (abortRequested()) {
842                 return;
843             }
844         }
845     }
846
847     // Helper for protected computeUpdatedPixels method
848
private static void computeUpdatedPixels(int sourceOffset,
849                                              int sourceExtent,
850                                              int destinationOffset,
851                                              int dstMin,
852                                              int dstMax,
853                                              int sourceSubsampling,
854                                              int passStart,
855                                              int passExtent,
856                                              int passPeriod,
857                                              int[] vals,
858                                              int offset) {
859
860         // We need to satisfy the congruences:
861
// dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
862
//
863
// src - passStart == 0 (mod passPeriod)
864
// src - sourceOffset == 0 (mod sourceSubsampling)
865
//
866
// subject to the inequalities:
867
//
868
// src >= passStart
869
// src < passStart + passExtent
870
// src >= sourceOffset
871
// src < sourceOffset + sourceExtent
872
// dst >= dstMin
873
// dst <= dstmax
874
//
875
// where
876
//
877
// dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
878
//
879
// For now we use a brute-force approach although we could
880
// attempt to analyze the congruences. If passPeriod and
881
// sourceSubsamling are relatively prime, the period will be
882
// their product. If they share a common factor, either the
883
// period will be equal to the larger value, or the sequences
884
// will be completely disjoint, depending on the relationship
885
// between passStart and sourceOffset. Since we only have to do this
886
// twice per image (once each for X and Y), it seems cheap enough
887
// to do it the straightforward way.
888

889         boolean gotPixel = false;
890         int firstDst = -1;
891         int secondDst = -1;
892         int lastDst = -1;
893
894         for (int i = 0; i < passExtent; i++) {
895             int src = passStart + i*passPeriod;
896             if (src < sourceOffset) {
897                 continue;
898             }
899             if ((src - sourceOffset) % sourceSubsampling != 0) {
900                 continue;
901             }
902             if (src >= sourceOffset + sourceExtent) {
903                 break;
904             }
905
906             int dst = destinationOffset +
907                 (src - sourceOffset)/sourceSubsampling;
908             if (dst < dstMin) {
909                 continue;
910             }
911             if (dst > dstMax) {
912                 break;
913             }
914
915             if (!gotPixel) {
916                 firstDst = dst; // Record smallest valid pixel
917
gotPixel = true;
918             } else if (secondDst == -1) {
919                 secondDst = dst; // Record second smallest valid pixel
920
}
921             lastDst = dst; // Record largest valid pixel
922
}
923
924         vals[offset] = firstDst;
925
926         // If we never saw a valid pixel, set width to 0
927
if (!gotPixel) {
928             vals[offset + 2] = 0;
929         } else {
930             vals[offset + 2] = lastDst - firstDst + 1;
931         }
932
933         // The period is given by the difference of any two adjacent pixels
934
vals[offset + 4] = Math.max(secondDst - firstDst, 1);
935     }
936
937     /**
938      * A utility method that computes the exact set of destination
939      * pixels that will be written during a particular decoding pass.
940      * The intent is to simplify the work done by readers in combining
941      * the source region, source subsampling, and destination offset
942      * information obtained from the <code>ImageReadParam</code> with
943      * the offsets and periods of a progressive or interlaced decoding
944      * pass.
945      *
946      * @param sourceRegion a <code>Rectangle</code> containing the
947      * source region being read, offset by the source subsampling
948      * offsets, and clipped against the source bounds, as returned by
949      * the <code>getSourceRegion</code> method.
950      * @param destinationOffset a <code>Point</code> containing the
951      * coordinates of the upper-left pixel to be written in the
952      * destination.
953      * @param dstMinX the smallest X coordinate (inclusive) of the
954      * destination <code>Raster</code>.
955      * @param dstMinY the smallest Y coordinate (inclusive) of the
956      * destination <code>Raster</code>.
957      * @param dstMaxX the largest X coordinate (inclusive) of the destination
958      * <code>Raster</code>.
959      * @param dstMaxY the largest Y coordinate (inclusive) of the destination
960      * <code>Raster</code>.
961      * @param sourceXSubsampling the X subsampling factor.
962      * @param sourceYSubsampling the Y subsampling factor.
963      * @param passXStart the smallest source X coordinate (inclusive)
964      * of the current progressive pass.
965      * @param passYStart the smallest source Y coordinate (inclusive)
966      * of the current progressive pass.
967      * @param passWidth the width in pixels of the current progressive
968      * pass.
969      * @param passHeight the height in pixels of the current progressive
970      * pass.
971      * @param passPeriodX the X period (horizontal spacing between
972      * pixels) of the current progressive pass.
973      * @param passPeriodY the Y period (vertical spacing between
974      * pixels) of the current progressive pass.
975      *
976      * @return an array of 6 <code>int</code>s containing the
977      * destination min X, min Y, width, height, X period and Y period
978      * of the region that will be updated.
979      */

980     private static int[] computeUpdatedPixels(Rectangle JavaDoc sourceRegion,
981                                               Point JavaDoc destinationOffset,
982                                               int dstMinX,
983                                               int dstMinY,
984                                               int dstMaxX,
985                                               int dstMaxY,
986                                               int sourceXSubsampling,
987                                               int sourceYSubsampling,
988                                               int passXStart,
989                                               int passYStart,
990                                               int passWidth,
991                                               int passHeight,
992                                               int passPeriodX,
993                                               int passPeriodY) {
994         int[] vals = new int[6];
995         computeUpdatedPixels(sourceRegion.x, sourceRegion.width,
996                              destinationOffset.x,
997                              dstMinX, dstMaxX, sourceXSubsampling,
998                              passXStart, passWidth, passPeriodX,
999                              vals, 0);
1000        computeUpdatedPixels(sourceRegion.y, sourceRegion.height,
1001                             destinationOffset.y,
1002                             dstMinY, dstMaxY, sourceYSubsampling,
1003                             passYStart, passHeight, passPeriodY,
1004                             vals, 1);
1005        return vals;
1006    }
1007
1008    private void updateImageProgress(int newPixels) {
1009        pixelsDone += newPixels;
1010        processImageProgress(100.0F*pixelsDone/totalPixels);
1011    }
1012
1013    private void decodePass(int passNum,
1014                            int xStart, int yStart,
1015                            int xStep, int yStep,
1016                            int passWidth, int passHeight) throws IOException JavaDoc {
1017
1018        if ((passWidth == 0) || (passHeight == 0)) {
1019            return;
1020        }
1021
1022        WritableRaster JavaDoc imRas = theImage.getWritableTile(0, 0);
1023        int dstMinX = imRas.getMinX();
1024        int dstMaxX = dstMinX + imRas.getWidth() - 1;
1025        int dstMinY = imRas.getMinY();
1026        int dstMaxY = dstMinY + imRas.getHeight() - 1;
1027
1028        // Determine which pixels will be updated in this pass
1029
int[] vals = computeUpdatedPixels(sourceRegion,
1030                                          destinationOffset,
1031                                          dstMinX, dstMinY,
1032                                          dstMaxX, dstMaxY,
1033                                          sourceXSubsampling,
1034                                          sourceYSubsampling,
1035                                          xStart, yStart,
1036                                          passWidth, passHeight,
1037                                          xStep, yStep);
1038        int updateMinX = vals[0];
1039        int updateMinY = vals[1];
1040        int updateWidth = vals[2];
1041        int updateXStep = vals[4];
1042        int updateYStep = vals[5];
1043
1044        int bitDepth = metadata.IHDR_bitDepth;
1045        int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
1046        int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
1047        bytesPerPixel *= inputBands;
1048        
1049        int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
1050        int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;
1051
1052        // If no pixels need updating, just skip the input data
1053
if (updateWidth == 0) {
1054            for (int srcY = 0; srcY < passHeight; srcY++) {
1055                // Update count of pixels read
1056
updateImageProgress(passWidth);
1057                pixelStream.skipBytes(1 + bytesPerRow);
1058            }
1059            return;
1060        }
1061        
1062        // Backwards map from destination pixels
1063
// (dstX = updateMinX + k*updateXStep)
1064
// to source pixels (sourceX), and then
1065
// to offset and skip in passRow (srcX and srcXStep)
1066
int sourceX =
1067            (updateMinX - destinationOffset.x)*sourceXSubsampling +
1068            sourceRegion.x;
1069        int srcX = (sourceX - xStart)/xStep;
1070        
1071        // Compute the step factor in the source
1072
int srcXStep = updateXStep*sourceXSubsampling/xStep;
1073
1074        byte[] byteData = null;
1075        short[] shortData = null;
1076        byte[] curr = new byte[bytesPerRow];
1077        byte[] prior = new byte[bytesPerRow];
1078
1079        // Create a 1-row tall Raster to hold the data
1080
WritableRaster JavaDoc passRow = createRaster(passWidth, 1, inputBands,
1081                                              eltsPerRow,
1082                                              bitDepth);
1083        
1084        // Create an array suitable for holding one pixel
1085
int[] ps = passRow.getPixel(0, 0, (int[])null);
1086        
1087        DataBuffer JavaDoc dataBuffer = passRow.getDataBuffer();
1088        int type = dataBuffer.getDataType();
1089        if (type == DataBuffer.TYPE_BYTE) {
1090            byteData = ((DataBufferByte JavaDoc)dataBuffer).getData();
1091        } else {
1092            shortData = ((DataBufferUShort JavaDoc)dataBuffer).getData();
1093        }
1094        
1095        processPassStarted(theImage,
1096                           passNum,
1097                           sourceMinProgressivePass,
1098                           sourceMaxProgressivePass,
1099                           updateMinX, updateMinY,
1100                           updateXStep, updateYStep,
1101                           destinationBands);
1102        
1103        // Handle source and destination bands
1104
if (sourceBands != null) {
1105            passRow = passRow.createWritableChild(0, 0,
1106                                                  passRow.getWidth(), 1,
1107                                                  0, 0,
1108                                                  sourceBands);
1109        }
1110        if (destinationBands != null) {
1111            imRas = imRas.createWritableChild(0, 0,
1112                                              imRas.getWidth(),
1113                                              imRas.getHeight(),
1114                                              0, 0,
1115                                              destinationBands);
1116        }
1117
1118        // Determine if all of the relevant output bands have the
1119
// same bit depth as the source data
1120
boolean adjustBitDepths = false;
1121        int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
1122        int numBands = outputSampleSize.length;
1123        for (int b = 0; b < numBands; b++) {
1124            if (outputSampleSize[b] != bitDepth) {
1125                adjustBitDepths = true;
1126                break;
1127            }
1128        }
1129
1130        // If the bit depths differ, create a lookup table per band to perform
1131
// the conversion
1132
int[][] scale = null;
1133        if (adjustBitDepths) {
1134            int maxInSample = (1 << bitDepth) - 1;
1135            int halfMaxInSample = maxInSample/2;
1136            scale = new int[numBands][];
1137            for (int b = 0; b < numBands; b++) {
1138                int maxOutSample = (1 << outputSampleSize[b]) - 1;
1139                scale[b] = new int[maxInSample + 1];
1140                for (int s = 0; s <= maxInSample; s++) {
1141                    scale[b][s] =
1142                        (s*maxOutSample + halfMaxInSample)/maxInSample;
1143                }
1144            }
1145        }
1146
1147        // Limit passRow to relevant area for the case where we
1148
// will can setRect to copy a contiguous span
1149
boolean useSetRect = srcXStep == 1 &&
1150            updateXStep == 1 &&
1151            !adjustBitDepths;
1152        if (useSetRect) {
1153            passRow = passRow.createWritableChild(srcX, 0,
1154                                                  updateWidth, 1,
1155                                                  0, 0,
1156                                                  null);
1157        }
1158
1159        // Decode the (sub)image row-by-row
1160
for (int srcY = 0; srcY < passHeight; srcY++) {
1161            // Update count of pixels read
1162
updateImageProgress(passWidth);
1163            
1164            // Read the filter type byte and a row of data
1165
int filter = pixelStream.read();
1166            try {
1167                // Swap curr and prior
1168
byte[] tmp = prior;
1169                prior = curr;
1170                curr = tmp;
1171
1172                pixelStream.readFully(curr, 0, bytesPerRow);
1173            } catch (java.util.zip.ZipException JavaDoc ze) {
1174                // TODO - throw a more meaningful exception
1175
throw ze;
1176            }
1177
1178            switch (filter) {
1179            case PNG_FILTER_NONE:
1180                break;
1181            case PNG_FILTER_SUB:
1182                decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
1183                break;
1184            case PNG_FILTER_UP:
1185                decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
1186                break;
1187            case PNG_FILTER_AVERAGE:
1188                decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
1189                                    bytesPerPixel);
1190                break;
1191            case PNG_FILTER_PAETH:
1192                decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
1193                                  bytesPerPixel);
1194                break;
1195            default:
1196                throw new IIOException JavaDoc("Unknown row filter type (= " +
1197                                       filter + ")!");
1198            }
1199
1200            // Copy data into passRow byte by byte
1201
if (bitDepth < 16) {
1202                System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
1203            } else {
1204                int idx = 0;
1205                for (int j = 0; j < eltsPerRow; j++) {
1206                    shortData[j] =
1207                        (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
1208                    idx += 2;
1209                }
1210            }
1211
1212            // True Y position in source
1213
int sourceY = srcY*yStep + yStart;
1214            if ((sourceY >= sourceRegion.y) &&
1215                (sourceY < sourceRegion.y + sourceRegion.height) &&
1216                (((sourceY - sourceRegion.y) %
1217                  sourceYSubsampling) == 0)) {
1218                
1219                int dstY = destinationOffset.y +
1220                    (sourceY - sourceRegion.y)/sourceYSubsampling;
1221                if (dstY < dstMinY) {
1222                    continue;
1223                }
1224                if (dstY > dstMaxY) {
1225                    break;
1226                }
1227
1228                if (useSetRect) {
1229                    imRas.setRect(updateMinX, dstY, passRow);
1230                } else {
1231                    int newSrcX = srcX;
1232
1233                    for (int dstX = updateMinX;
1234                         dstX < updateMinX + updateWidth;
1235                         dstX += updateXStep) {
1236                        
1237                        passRow.getPixel(newSrcX, 0, ps);
1238                        if (adjustBitDepths) {
1239                            for (int b = 0; b < numBands; b++) {
1240                                ps[b] = scale[b][ps[b]];
1241                            }
1242                        }
1243                        imRas.setPixel(dstX, dstY, ps);
1244                        newSrcX += srcXStep;
1245                    }
1246                }
1247                
1248                processImageUpdate(theImage,
1249                                   updateMinX, dstY,
1250                                   updateWidth, 1,
1251                                   updateXStep, updateYStep,
1252                                   destinationBands);
1253
1254                // If read has been aborted, just return
1255
// processReadAborted will be called later
1256
if (abortRequested()) {
1257                    return;
1258                }
1259            }
1260        }
1261        
1262        processPassComplete(theImage);
1263    }
1264
1265    private void decodeImage()
1266        throws IOException JavaDoc, IIOException JavaDoc {
1267        int width = metadata.IHDR_width;
1268        int height = metadata.IHDR_height;
1269
1270        this.pixelsDone = 0;
1271        this.totalPixels = width*height;
1272
1273        clearAbortRequest();
1274
1275        if (metadata.IHDR_interlaceMethod == 0) {
1276            decodePass(0, 0, 0, 1, 1, width, height);
1277        } else {
1278            for (int i = 0; i <= sourceMaxProgressivePass; i++) {
1279                int XOffset = adam7XOffset[i];
1280                int YOffset = adam7YOffset[i];
1281                int XSubsampling = adam7XSubsampling[i];
1282                int YSubsampling = adam7YSubsampling[i];
1283                int xbump = adam7XSubsampling[i + 1] - 1;
1284                int ybump = adam7YSubsampling[i + 1] - 1;
1285
1286                if (i >= sourceMinProgressivePass) {
1287                    decodePass(i,
1288                               XOffset,
1289                               YOffset,
1290                               XSubsampling,
1291                               YSubsampling,
1292                               (width + xbump)/XSubsampling,
1293                               (height + ybump)/YSubsampling);
1294                } else {
1295                    skipPass((width + xbump)/XSubsampling,
1296                             (height + ybump)/YSubsampling);
1297                }
1298
1299                // If read has been aborted, just return
1300
// processReadAborted will be called later
1301
if (abortRequested()) {
1302                    return;
1303                }
1304            }
1305        }
1306    }
1307
1308    private void readImage(ImageReadParam JavaDoc param) throws IIOException JavaDoc {
1309        readMetadata();
1310
1311        int width = metadata.IHDR_width;
1312        int height = metadata.IHDR_height;
1313
1314        // Init default values
1315
sourceRegion = getSourceRegion(param, width, height);
1316        sourceXSubsampling = 1;
1317        sourceYSubsampling = 1;
1318        sourceMinProgressivePass = 0;
1319        sourceMaxProgressivePass = 6;
1320        sourceBands = null;
1321        destinationBands = null;
1322        destinationOffset = new Point JavaDoc(0, 0);
1323
1324        // If an ImageReadParam is available, get values from it
1325
if (param != null) {
1326            sourceXSubsampling = param.getSourceXSubsampling();
1327            sourceYSubsampling = param.getSourceYSubsampling();
1328
1329            sourceMinProgressivePass =
1330                Math.max(param.getSourceMinProgressivePass(), 0);
1331            sourceMaxProgressivePass =
1332                Math.min(param.getSourceMaxProgressivePass(), 6);
1333
1334            sourceBands = param.getSourceBands();
1335            destinationBands = param.getDestinationBands();
1336            destinationOffset = param.getDestinationOffset();
1337        }
1338
1339        try {
1340            stream.seek(imageStartPosition);
1341
1342            Enumeration JavaDoc e = new PNGImageDataEnumeration(stream);
1343            InputStream JavaDoc is = new SequenceInputStream JavaDoc(e);
1344            is = new InflaterInputStream JavaDoc(is, new Inflater JavaDoc());
1345            is = new BufferedInputStream JavaDoc(is);
1346            this.pixelStream = new DataInputStream JavaDoc(is);
1347
1348            theImage = getDestination(param,
1349                                      getImageTypes(0),
1350                                      width,
1351                                      height);
1352
1353            // At this point the header has been read and we know
1354
// how many bands are in the image, so perform checking
1355
// of the read param.
1356
int colorType = metadata.IHDR_colorType;
1357            checkReadParamBandSettings(param,
1358                                       inputBandsForColorType[colorType],
1359                                      theImage.getSampleModel().getNumBands());
1360
1361            processImageStarted(0);
1362            decodeImage();
1363            if (abortRequested()) {
1364                processReadAborted();
1365            } else {
1366                processImageComplete();
1367            }
1368        } catch (IOException JavaDoc e) {
1369            e.printStackTrace();
1370            throw new IIOException JavaDoc("Error reading PNG image data", e);
1371        }
1372    }
1373
1374    public int getNumImages(boolean allowSearch) throws IIOException JavaDoc {
1375        if (stream == null) {
1376            throw new IllegalStateException JavaDoc("No input source set!");
1377        }
1378        if (seekForwardOnly && allowSearch) {
1379            throw new IllegalStateException JavaDoc
1380                ("seekForwardOnly and allowSearch can't both be true!");
1381        }
1382        return 1;
1383    }
1384
1385    public int getWidth(int imageIndex) throws IIOException JavaDoc {
1386        readHeader();
1387
1388        return metadata.IHDR_width;
1389    }
1390
1391    public int getHeight(int imageIndex) throws IIOException JavaDoc {
1392        readHeader();
1393
1394        return metadata.IHDR_height;
1395    }
1396
1397    public Iterator JavaDoc getImageTypes(int imageIndex) throws IIOException JavaDoc {
1398        if (imageIndex != 0) {
1399            throw new IndexOutOfBoundsException JavaDoc("imageIndex != 0!");
1400        }
1401
1402        readHeader();
1403
1404        ArrayList JavaDoc l = new ArrayList JavaDoc(1); // List of ImageTypeSpecifiers
1405
ColorSpace JavaDoc rgb;
1406        ColorSpace JavaDoc gray;
1407        int[] bandOffsets;
1408        
1409        int bitDepth = metadata.IHDR_bitDepth;
1410        int colorType = metadata.IHDR_colorType;
1411
1412        int dataType;
1413        if (bitDepth <= 8) {
1414            dataType = DataBuffer.TYPE_BYTE;
1415        } else {
1416            dataType = DataBuffer.TYPE_USHORT;
1417        }
1418
1419        switch (colorType) {
1420        case PNG_COLOR_GRAY:
1421            // Packed grayscale
1422
l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
1423                                                     dataType,
1424                                                     false));
1425            break;
1426
1427        case PNG_COLOR_RGB:
1428            // Component R, G, B
1429
rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1430            bandOffsets = new int[3];
1431            bandOffsets[0] = 0;
1432            bandOffsets[1] = 1;
1433            bandOffsets[2] = 2;
1434            l.add(ImageTypeSpecifier.createInterleaved(rgb,
1435                                                       bandOffsets,
1436                                                       dataType,
1437                                                       false,
1438                                                       false));
1439            break;
1440
1441        case PNG_COLOR_PALETTE:
1442            readMetadata(); // Need tRNS chunk
1443

1444            /*
1445             * The PLTE chunk spec says:
1446             *
1447             * The number of palette entries must not exceed the range that
1448             * can be represented in the image bit depth (for example, 2^4 = 16
1449             * for a bit depth of 4). It is permissible to have fewer entries
1450             * than the bit depth would allow. In that case, any out-of-range
1451             * pixel value found in the image data is an error.
1452             *
1453             * http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.PLTE
1454             *
1455             * Consequently, the case when the palette length is smaller than
1456             * 2^bitDepth is legal in the view of PNG spec.
1457             *
1458             * However the spec of createIndexed() method demands the exact
1459             * equality of the palette lengh and number of possible palette
1460             * entries (2^bitDepth).
1461             *
1462             * {@link javax.imageio.ImageTypeSpecifier.html#createIndexed}
1463             *
1464             * In order to avoid this contradiction we need to extend the
1465             * palette arrays to the limit defined by the bitDepth.
1466             */

1467
1468            int plength = 1 << bitDepth;
1469
1470            byte[] red = metadata.PLTE_red;
1471            byte[] green = metadata.PLTE_green;
1472            byte[] blue = metadata.PLTE_blue;
1473
1474            if (metadata.PLTE_red.length < plength) {
1475                red = new byte[plength];
1476                System.arraycopy(metadata.PLTE_red, 0, red, 0,
1477                                 metadata.PLTE_red.length);
1478                Arrays.fill(red, metadata.PLTE_red.length, plength,
1479                            metadata.PLTE_red[metadata.PLTE_red.length - 1]);
1480
1481                green = new byte[plength];
1482                System.arraycopy(metadata.PLTE_green, 0, green, 0,
1483                                 Math.min(metadata.PLTE_green.length, plength));
1484
1485                Arrays.fill(green, metadata.PLTE_green.length, plength,
1486                            metadata.PLTE_green[metadata.PLTE_green.length - 1]);
1487
1488                blue = new byte[plength];
1489                System.arraycopy(metadata.PLTE_blue, 0, blue, 0,
1490                                 Math.min(metadata.PLTE_blue.length, plength));
1491                Arrays.fill(blue, metadata.PLTE_blue.length, plength,
1492                            metadata.PLTE_blue[metadata.PLTE_blue.length - 1]);
1493
1494            }
1495
1496
1497            // Alpha from tRNS chunk may have fewer entries than
1498
// the RGB LUTs from the PLTE chunk; if so, pad with
1499
// 255.
1500
byte[] alpha = null;
1501            if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) {
1502                if (metadata.tRNS_alpha.length == red.length) {
1503                    alpha = metadata.tRNS_alpha;
1504                } else {
1505                    alpha = new byte[red.length];
1506                    System.arraycopy(metadata.tRNS_alpha, 0, alpha, 0,
1507                                     Math.min(metadata.tRNS_alpha.length, red.length));
1508                    Arrays.fill(alpha,
1509                                metadata.tRNS_alpha.length,
1510                                red.length, (byte)255);
1511                }
1512            }
1513
1514            l.add(ImageTypeSpecifier.createIndexed(red, green,
1515                                                   blue, alpha,
1516                                                   bitDepth,
1517                                                   DataBuffer.TYPE_BYTE));
1518            break;
1519
1520        case PNG_COLOR_GRAY_ALPHA:
1521            // Component G, A
1522
gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
1523            bandOffsets = new int[2];
1524            bandOffsets[0] = 0;
1525            bandOffsets[1] = 1;
1526            l.add(ImageTypeSpecifier.createInterleaved(gray,
1527                                                       bandOffsets,
1528                                                       dataType,
1529                                                       true,
1530                                                       false));
1531            break;
1532
1533        case PNG_COLOR_RGB_ALPHA:
1534            // Component R, G, B, A (non-premultiplied)
1535
rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1536            bandOffsets = new int[4];
1537            bandOffsets[0] = 0;
1538            bandOffsets[1] = 1;
1539            bandOffsets[2] = 2;
1540            bandOffsets[3] = 3;
1541
1542            l.add(ImageTypeSpecifier.createInterleaved(rgb,
1543                                                       bandOffsets,
1544                                                       dataType,
1545                                                       true,
1546                                                       false));
1547            break;
1548
1549        default:
1550            break;
1551        }
1552
1553        return l.iterator();
1554    }
1555
1556    public ImageReadParam JavaDoc getDefaultReadParam() {
1557        return new ImageReadParam JavaDoc();
1558    }
1559
1560    public IIOMetadata JavaDoc getStreamMetadata()
1561        throws IIOException JavaDoc {
1562        return null;
1563    }
1564
1565    public IIOMetadata JavaDoc getImageMetadata(int imageIndex) throws IIOException JavaDoc {
1566        if (imageIndex != 0) {
1567            throw new IndexOutOfBoundsException JavaDoc("imageIndex != 0!");
1568        }
1569        readMetadata();
1570        return metadata;
1571    }
1572    
1573    public BufferedImage JavaDoc read(int imageIndex, ImageReadParam JavaDoc param)
1574        throws IIOException JavaDoc {
1575        if (imageIndex != 0) {
1576            throw new IndexOutOfBoundsException JavaDoc("imageIndex != 0!");
1577        }
1578
1579        readImage(param);
1580        return theImage;
1581    }
1582    
1583    public void reset() {
1584        super.reset();
1585        resetStreamSettings();
1586    }
1587
1588    private void resetStreamSettings() {
1589        gotHeader = false;
1590        gotMetadata = false;
1591        metadata = null;
1592        pixelStream = null;
1593    }
1594}
1595
Popular Tags