KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > codec > PNGImageDecoder


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.codec;
19
20 import java.awt.Color JavaDoc;
21 import java.awt.Point JavaDoc;
22 import java.awt.Transparency JavaDoc;
23 import java.awt.color.ColorSpace JavaDoc;
24 import java.awt.image.ColorModel JavaDoc;
25 import java.awt.image.ComponentColorModel JavaDoc;
26 import java.awt.image.DataBuffer JavaDoc;
27 import java.awt.image.DataBufferByte JavaDoc;
28 import java.awt.image.DataBufferUShort JavaDoc;
29 import java.awt.image.IndexColorModel JavaDoc;
30 import java.awt.image.Raster JavaDoc;
31 import java.awt.image.RenderedImage JavaDoc;
32 import java.awt.image.SampleModel JavaDoc;
33 import java.awt.image.WritableRaster JavaDoc;
34 import java.io.BufferedInputStream JavaDoc;
35 import java.io.ByteArrayInputStream JavaDoc;
36 import java.io.DataInputStream JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39 import java.io.SequenceInputStream JavaDoc;
40 import java.util.Date JavaDoc;
41 import java.util.GregorianCalendar JavaDoc;
42 import java.util.TimeZone JavaDoc;
43 import java.util.Vector JavaDoc;
44 import java.util.zip.Inflater JavaDoc;
45 import java.util.zip.InflaterInputStream JavaDoc;
46
47 /**
48  */

49 public class PNGImageDecoder extends ImageDecoderImpl {
50
51     public PNGImageDecoder(InputStream JavaDoc input,
52                            PNGDecodeParam param) {
53         super(input, param);
54     }
55
56     public RenderedImage JavaDoc decodeAsRenderedImage(int page) throws IOException JavaDoc {
57         if (page != 0) {
58             throw new IOException JavaDoc(PropertyUtil.getString("PNGImageDecoder19"));
59         }
60         return new PNGImage(input, (PNGDecodeParam)param);
61     }
62 }
63
64 class PNGChunk {
65     int length;
66     int type;
67     byte[] data;
68     int crc;
69
70     String JavaDoc typeString;
71
72     public PNGChunk(int length, int type, byte[] data, int crc) {
73         this.length = length;
74         this.type = type;
75         this.data = data;
76         this.crc = crc;
77
78         typeString = new String JavaDoc();
79         typeString += (char)(type >> 24);
80         typeString += (char)((type >> 16) & 0xff);
81         typeString += (char)((type >> 8) & 0xff);
82         typeString += (char)(type & 0xff);
83     }
84
85     public int getLength() {
86         return length;
87     }
88
89     public int getType() {
90         return type;
91     }
92
93     public String JavaDoc getTypeString() {
94         return typeString;
95     }
96
97     public byte[] getData() {
98         return data;
99     }
100
101     public byte getByte(int offset) {
102         return data[offset];
103     }
104
105     public int getInt1(int offset) {
106         return data[offset] & 0xff;
107     }
108
109     public int getInt2(int offset) {
110         return ((data[offset] & 0xff) << 8) |
111             (data[offset + 1] & 0xff);
112     }
113
114     public int getInt4(int offset) {
115         return ((data[offset] & 0xff) << 24) |
116             ((data[offset + 1] & 0xff) << 16) |
117             ((data[offset + 2] & 0xff) << 8) |
118             (data[offset + 3] & 0xff);
119     }
120
121     public String JavaDoc getString4(int offset) {
122         String JavaDoc s = new String JavaDoc();
123         s += (char)data[offset];
124         s += (char)data[offset + 1];
125         s += (char)data[offset + 2];
126         s += (char)data[offset + 3];
127         return s;
128     }
129
130     public boolean isType(String JavaDoc typeName) {
131         return typeString.equals(typeName);
132     }
133 }
134
135 /**
136  * TO DO:
137  *
138  * zTXt chunks
139  *
140  */

141 class PNGImage extends SimpleRenderedImage {
142
143     public static final int PNG_COLOR_GRAY = 0;
144     public static final int PNG_COLOR_RGB = 2;
145     public static final int PNG_COLOR_PALETTE = 3;
146     public static final int PNG_COLOR_GRAY_ALPHA = 4;
147     public static final int PNG_COLOR_RGB_ALPHA = 6;
148
149     private static final String JavaDoc[] colorTypeNames = {
150         "Grayscale", "Error", "Truecolor", "Index",
151         "Grayscale with alpha", "Error", "Truecolor with alpha"
152     };
153
154     public static final int PNG_FILTER_NONE = 0;
155     public static final int PNG_FILTER_SUB = 1;
156     public static final int PNG_FILTER_UP = 2;
157     public static final int PNG_FILTER_AVERAGE = 3;
158     public static final int PNG_FILTER_PAETH = 4;
159
160     private static final int RED_OFFSET = 2;
161     private static final int GREEN_OFFSET = 1;
162     private static final int BLUE_OFFSET = 0;
163
164     private int[][] bandOffsets = {
165         null,
166         { 0 }, // G
167
{ 0, 1 }, // GA in GA order
168
{ 0, 1, 2 }, // RGB in RGB order
169
{ 0, 1, 2, 3 } // RGBA in RGBA order
170
};
171
172     private int bitDepth;
173     private int colorType;
174
175     private int compressionMethod;
176     private int filterMethod;
177     private int interlaceMethod;
178     
179     private int paletteEntries;
180     private byte[] redPalette;
181     private byte[] greenPalette;
182     private byte[] bluePalette;
183     private byte[] alphaPalette;
184
185     private int bkgdRed;
186     private int bkgdGreen;
187     private int bkgdBlue;
188
189     private int grayTransparentAlpha;
190     private int redTransparentAlpha;
191     private int greenTransparentAlpha;
192     private int blueTransparentAlpha;
193
194     private int maxOpacity;
195
196     private int[] significantBits = null;
197
198     private boolean hasBackground = false;
199
200     // Parameter information
201

202     // If true, the user wants destination alpha where applicable.
203
private boolean suppressAlpha = false;
204
205     // If true, perform palette lookup internally
206
private boolean expandPalette = false;
207     
208     // If true, output < 8 bit gray images in 8 bit components format
209
private boolean output8BitGray = false;
210
211     // Create an alpha channel in the destination color model.
212
private boolean outputHasAlphaPalette = false;
213
214     // Perform gamma correction on the image
215
private boolean performGammaCorrection = false;
216
217     // Expand GA to GGGA for compatbility with Java2D
218
private boolean expandGrayAlpha = false;
219
220     // Produce an instance of PNGEncodeParam
221
private boolean generateEncodeParam = false;
222
223     // PNGDecodeParam controlling decode process
224
private PNGDecodeParam decodeParam = null;
225
226     // PNGEncodeParam to store file details in
227
private PNGEncodeParam encodeParam = null;
228
229     private boolean emitProperties = true;
230
231     private float fileGamma = 45455/100000.0F;
232
233     private float userExponent = 1.0F;
234
235     private float displayExponent = 2.2F;
236
237     private float[] chromaticity = null;
238
239     private int sRGBRenderingIntent = -1;
240
241     // Post-processing step implied by above parameters
242
private int postProcess = POST_NONE;
243
244     // Possible post-processing steps
245

246     // Do nothing
247
private static final int POST_NONE = 0;
248
249     // Gamma correct only
250
private static final int POST_GAMMA = 1;
251
252     // Push gray values through grayLut to expand to 8 bits
253
private static final int POST_GRAY_LUT = 2;
254
255     // Push gray values through grayLut to expand to 8 bits, add alpha
256
private static final int POST_GRAY_LUT_ADD_TRANS = 3;
257
258     // Push palette value through R,G,B lookup tables
259
private static final int POST_PALETTE_TO_RGB = 4;
260
261     // Push palette value through R,G,B,A lookup tables
262
private static final int POST_PALETTE_TO_RGBA = 5;
263
264     // Add transparency to a given gray value (w/ optional gamma)
265
private static final int POST_ADD_GRAY_TRANS = 6;
266
267     // Add transparency to a given RGB value (w/ optional gamma)
268
private static final int POST_ADD_RGB_TRANS = 7;
269
270     // Remove the alpha channel from a gray image (w/ optional gamma)
271
private static final int POST_REMOVE_GRAY_TRANS = 8;
272
273     // Remove the alpha channel from an RGB image (w/optional gamma)
274
private static final int POST_REMOVE_RGB_TRANS = 9;
275
276     // Mask to add expansion of GA -> GGGA
277
private static final int POST_EXP_MASK = 16;
278
279     // Expand gray to G/G/G
280
private static final int POST_GRAY_ALPHA_EXP =
281         POST_NONE | POST_EXP_MASK;
282
283     // Expand gray to G/G/G through a gamma lut
284
private static final int POST_GAMMA_EXP =
285         POST_GAMMA | POST_EXP_MASK;
286
287     // Push gray values through grayLut to expand to 8 bits, expand, add alpha
288
private static final int POST_GRAY_LUT_ADD_TRANS_EXP =
289         POST_GRAY_LUT_ADD_TRANS | POST_EXP_MASK;
290
291     // Add transparency to a given gray value, expand
292
private static final int POST_ADD_GRAY_TRANS_EXP =
293         POST_ADD_GRAY_TRANS | POST_EXP_MASK;
294
295     private Vector JavaDoc streamVec = new Vector JavaDoc();
296     private DataInputStream JavaDoc dataStream;
297
298     private int bytesPerPixel; // number of bytes per input pixel
299
private int inputBands;
300     private int outputBands;
301
302     // Number of private chunks
303
private int chunkIndex = 0;
304
305     private Vector JavaDoc textKeys = new Vector JavaDoc();
306     private Vector JavaDoc textStrings = new Vector JavaDoc();
307
308     private Vector JavaDoc ztextKeys = new Vector JavaDoc();
309     private Vector JavaDoc ztextStrings = new Vector JavaDoc();
310
311     private WritableRaster JavaDoc theTile;
312
313     private int[] gammaLut = null;
314
315     private void initGammaLut(int bits) {
316         double exp = (double)userExponent/(fileGamma*displayExponent);
317         int numSamples = 1 << bits;
318         int maxOutSample = (bits == 16) ? 65535 : 255;
319
320         gammaLut = new int[numSamples];
321         for (int i = 0; i < numSamples; i++) {
322             double gbright = (double)i/(numSamples - 1);
323             double gamma = Math.pow(gbright, exp);
324             int igamma = (int)(gamma*maxOutSample + 0.5);
325             if (igamma > maxOutSample) {
326                 igamma = maxOutSample;
327             }
328             gammaLut[i] = igamma;
329         }
330     }
331
332     private final byte[][] expandBits = {
333         null,
334         { (byte)0x00, (byte)0xff },
335         { (byte)0x00, (byte)0x55, (byte)0xaa, (byte)0xff },
336         null,
337         { (byte)0x00, (byte)0x11, (byte)0x22, (byte)0x33,
338           (byte)0x44, (byte)0x55, (byte)0x66, (byte)0x77,
339           (byte)0x88, (byte)0x99, (byte)0xaa, (byte)0xbb,
340           (byte)0xcc, (byte)0xdd, (byte)0xee, (byte)0xff }
341     };
342
343     private int[] grayLut = null;
344
345     private void initGrayLut(int bits) {
346         int len = 1 << bits;
347         grayLut = new int[len];
348
349         if (performGammaCorrection) {
350             for (int i = 0; i < len; i++) {
351                 grayLut[i] = gammaLut[i];
352             }
353         } else {
354             for (int i = 0; i < len; i++) {
355                 grayLut[i] = expandBits[bits][i];
356             }
357         }
358     }
359
360     public PNGImage(InputStream JavaDoc stream, PNGDecodeParam decodeParam)
361         throws IOException JavaDoc {
362
363         if (!stream.markSupported()) {
364             stream = new BufferedInputStream JavaDoc(stream);
365         }
366         DataInputStream JavaDoc distream = new DataInputStream JavaDoc(stream);
367
368         if (decodeParam == null) {
369             decodeParam = new PNGDecodeParam();
370         }
371         this.decodeParam = decodeParam;
372
373         // Get parameter values
374
this.suppressAlpha = decodeParam.getSuppressAlpha();
375         this.expandPalette = decodeParam.getExpandPalette();
376         this.output8BitGray = decodeParam.getOutput8BitGray();
377         this.expandGrayAlpha = decodeParam.getExpandGrayAlpha();
378         if (decodeParam.getPerformGammaCorrection()) {
379             this.userExponent = decodeParam.getUserExponent();
380             this.displayExponent = decodeParam.getDisplayExponent();
381             performGammaCorrection = true;
382             output8BitGray = true;
383         }
384         this.generateEncodeParam = decodeParam.getGenerateEncodeParam();
385         
386         if (emitProperties) {
387             properties.put("file_type", "PNG v. 1.0");
388         }
389
390         try {
391             long magic = distream.readLong();
392             if (magic != 0x89504e470d0a1a0aL) {
393                 String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder0");
394                 throw new RuntimeException JavaDoc(msg);
395             }
396         } catch (Exception JavaDoc e) {
397             e.printStackTrace();
398             String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder1");
399             throw new RuntimeException JavaDoc(msg);
400         }
401             
402         do {
403             try {
404                 PNGChunk chunk;
405                 
406                 String JavaDoc chunkType = getChunkType(distream);
407                 if (chunkType.equals("IHDR")) {
408                     chunk = readChunk(distream);
409                     parse_IHDR_chunk(chunk);
410                 } else if (chunkType.equals("PLTE")) {
411                     chunk = readChunk(distream);
412                     parse_PLTE_chunk(chunk);
413                 } else if (chunkType.equals("IDAT")) {
414                     chunk = readChunk(distream);
415                     streamVec.add(new ByteArrayInputStream JavaDoc(chunk.getData()));
416                 } else if (chunkType.equals("IEND")) {
417                     chunk = readChunk(distream);
418                     parse_IEND_chunk(chunk);
419                     break; // fall through to the bottom
420
} else if (chunkType.equals("bKGD")) {
421                     chunk = readChunk(distream);
422                     parse_bKGD_chunk(chunk);
423                 } else if (chunkType.equals("cHRM")) {
424                     chunk = readChunk(distream);
425                     parse_cHRM_chunk(chunk);
426                 } else if (chunkType.equals("gAMA")) {
427                     chunk = readChunk(distream);
428                     parse_gAMA_chunk(chunk);
429                 } else if (chunkType.equals("hIST")) {
430                     chunk = readChunk(distream);
431                     parse_hIST_chunk(chunk);
432                 } else if (chunkType.equals("iCCP")) {
433                     chunk = readChunk(distream);
434                     parse_iCCP_chunk(chunk);
435                 } else if (chunkType.equals("pHYs")) {
436                     chunk = readChunk(distream);
437                     parse_pHYs_chunk(chunk);
438                 } else if (chunkType.equals("sBIT")) {
439                     chunk = readChunk(distream);
440                     parse_sBIT_chunk(chunk);
441                 } else if (chunkType.equals("sRGB")) {
442                     chunk = readChunk(distream);
443                     parse_sRGB_chunk(chunk);
444                 } else if (chunkType.equals("tEXt")) {
445                     chunk = readChunk(distream);
446                     parse_tEXt_chunk(chunk);
447                 } else if (chunkType.equals("tIME")) {
448                     chunk = readChunk(distream);
449                     parse_tIME_chunk(chunk);
450                 } else if (chunkType.equals("tRNS")) {
451                     chunk = readChunk(distream);
452                     parse_tRNS_chunk(chunk);
453                 } else if (chunkType.equals("zTXt")) {
454                     chunk = readChunk(distream);
455                     parse_zTXt_chunk(chunk);
456                 } else {
457                     chunk = readChunk(distream);
458                     // Output the chunk data in raw form
459

460                     String JavaDoc type = chunk.getTypeString();
461                     byte[] data = chunk.getData();
462                     if (encodeParam != null) {
463                         encodeParam.addPrivateChunk(type, data);
464                     }
465                     if (emitProperties) {
466                         String JavaDoc key = "chunk_" + chunkIndex++ + ":" + type;
467                         properties.put(key.toLowerCase(), data);
468                     }
469                 }
470             } catch (Exception JavaDoc e) {
471                 e.printStackTrace();
472                 String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder2");
473                 throw new RuntimeException JavaDoc(msg);
474             }
475         } while (true);
476
477         // Final post-processing
478

479         if (significantBits == null) {
480             significantBits = new int[inputBands];
481             for (int i = 0; i < inputBands; i++) {
482                 significantBits[i] = bitDepth;
483             }
484
485             if (emitProperties) {
486                 properties.put("significant_bits", significantBits);
487             }
488         }
489     }
490
491     private static String JavaDoc getChunkType(DataInputStream JavaDoc distream) {
492         try {
493             distream.mark(8);
494             /* int length = */ distream.readInt();
495             int type = distream.readInt();
496             distream.reset();
497
498             String JavaDoc typeString = new String JavaDoc();
499             typeString += (char)(type >> 24);
500             typeString += (char)((type >> 16) & 0xff);
501             typeString += (char)((type >> 8) & 0xff);
502             typeString += (char)(type & 0xff);
503             return typeString;
504         } catch (Exception JavaDoc e) {
505             e.printStackTrace();
506             return null;
507         }
508     }
509
510     private static PNGChunk readChunk(DataInputStream JavaDoc distream) {
511         try {
512             int length = distream.readInt();
513             int type = distream.readInt();
514             byte[] data = new byte[length];
515             distream.readFully(data);
516             int crc = distream.readInt();
517             
518             return new PNGChunk(length, type, data, crc);
519         } catch (Exception JavaDoc e) {
520             e.printStackTrace();
521             return null;
522         }
523     }
524
525     private void parse_IHDR_chunk(PNGChunk chunk) {
526         tileWidth = width = chunk.getInt4(0);
527         tileHeight = height = chunk.getInt4(4);
528
529         bitDepth = chunk.getInt1(8);
530         
531         if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4) &&
532             (bitDepth != 8) && (bitDepth != 16)) {
533             // Error -- bad bit depth
534
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder3");
535             throw new RuntimeException JavaDoc(msg);
536         }
537         maxOpacity = (1 << bitDepth) - 1;
538
539         colorType = chunk.getInt1(9);
540         if ((colorType != PNG_COLOR_GRAY) &&
541             (colorType != PNG_COLOR_RGB) &&
542             (colorType != PNG_COLOR_PALETTE) &&
543             (colorType != PNG_COLOR_GRAY_ALPHA) &&
544             (colorType != PNG_COLOR_RGB_ALPHA)) {
545             System.out.println(PropertyUtil.getString("PNGImageDecoder4"));
546         }
547
548         if ((colorType == PNG_COLOR_RGB) && (bitDepth < 8)) {
549             // Error -- RGB images must have 8 or 16 bits
550
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder5");
551             throw new RuntimeException JavaDoc(msg);
552         }
553
554         if ((colorType == PNG_COLOR_PALETTE) && (bitDepth == 16)) {
555             // Error -- palette images must have < 16 bits
556
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder6");
557             throw new RuntimeException JavaDoc(msg);
558         }
559
560         if ((colorType == PNG_COLOR_GRAY_ALPHA) && (bitDepth < 8)) {
561             // Error -- gray/alpha images must have >= 8 bits
562
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder7");
563             throw new RuntimeException JavaDoc(msg);
564         }
565
566         if ((colorType == PNG_COLOR_RGB_ALPHA) && (bitDepth < 8)) {
567             // Error -- RGB/alpha images must have >= 8 bits
568
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder8");
569             throw new RuntimeException JavaDoc(msg);
570         }
571
572         if (emitProperties) {
573             properties.put("color_type", colorTypeNames[colorType]);
574         }
575
576         if (generateEncodeParam) {
577             if (colorType == PNG_COLOR_PALETTE) {
578                 encodeParam = new PNGEncodeParam.Palette();
579             } else if (colorType == PNG_COLOR_GRAY ||
580                        colorType == PNG_COLOR_GRAY_ALPHA) {
581                 encodeParam = new PNGEncodeParam.Gray();
582             } else {
583                 encodeParam = new PNGEncodeParam.RGB();
584             }
585             decodeParam.setEncodeParam(encodeParam);
586         }
587
588         if (encodeParam != null) {
589             encodeParam.setBitDepth(bitDepth);
590         }
591         if (emitProperties) {
592             properties.put("bit_depth", new Integer JavaDoc(bitDepth));
593         }
594
595         if (performGammaCorrection) {
596             // Assume file gamma is 1/2.2 unless we get a gAMA chunk
597
float gamma = (1.0F/2.2F)*(displayExponent/userExponent);
598             if (encodeParam != null) {
599                 encodeParam.setGamma(gamma);
600             }
601             if (emitProperties) {
602                 properties.put("gamma", new Float JavaDoc(gamma));
603             }
604         }
605
606         compressionMethod = chunk.getInt1(10);
607         if (compressionMethod != 0) {
608             // Error -- only know about compression method 0
609
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder9");
610             throw new RuntimeException JavaDoc(msg);
611         }
612
613         filterMethod = chunk.getInt1(11);
614         if (filterMethod != 0) {
615             // Error -- only know about filter method 0
616
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder10");
617             throw new RuntimeException JavaDoc(msg);
618         }
619
620         interlaceMethod = chunk.getInt1(12);
621         if (interlaceMethod == 0) {
622             if (encodeParam != null) {
623                 encodeParam.setInterlacing(false);
624             }
625             if (emitProperties) {
626                 properties.put("interlace_method", "None");
627             }
628         } else if (interlaceMethod == 1) {
629             if (encodeParam != null) {
630                 encodeParam.setInterlacing(true);
631             }
632             if (emitProperties) {
633                 properties.put("interlace_method", "Adam7");
634             }
635         } else {
636             // Error -- only know about Adam7 interlacing
637
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder11");
638             throw new RuntimeException JavaDoc(msg);
639         }
640         
641         bytesPerPixel = (bitDepth == 16) ? 2 : 1;
642
643         switch (colorType) {
644         case PNG_COLOR_GRAY:
645             inputBands = 1;
646             outputBands = 1;
647
648             if (output8BitGray && (bitDepth < 8)) {
649                 postProcess = POST_GRAY_LUT;
650             } else if (performGammaCorrection) {
651                 postProcess = POST_GAMMA;
652             } else {
653                 postProcess = POST_NONE;
654             }
655             break;
656
657         case PNG_COLOR_RGB:
658             inputBands = 3;
659             bytesPerPixel *= 3;
660             outputBands = 3;
661
662             if (performGammaCorrection) {
663                 postProcess = POST_GAMMA;
664             } else {
665                 postProcess = POST_NONE;
666             }
667             break;
668
669         case PNG_COLOR_PALETTE:
670             inputBands = 1;
671             bytesPerPixel = 1;
672             outputBands = expandPalette ? 3 : 1;
673
674             if (expandPalette) {
675                 postProcess = POST_PALETTE_TO_RGB;
676             } else {
677                 postProcess = POST_NONE;
678             }
679             break;
680
681         case PNG_COLOR_GRAY_ALPHA:
682             inputBands = 2;
683             bytesPerPixel *= 2;
684
685             if (suppressAlpha) {
686                 outputBands = 1;
687                 postProcess = POST_REMOVE_GRAY_TRANS;
688             } else {
689                 if (performGammaCorrection) {
690                     postProcess = POST_GAMMA;
691                 } else {
692                     postProcess = POST_NONE;
693                 }
694                 if (expandGrayAlpha) {
695                     postProcess |= POST_EXP_MASK;
696                     outputBands = 4;
697                 } else {
698                     outputBands = 2;
699                 }
700             }
701             break;
702
703         case PNG_COLOR_RGB_ALPHA:
704             inputBands = 4;
705             bytesPerPixel *= 4;
706             outputBands = (!suppressAlpha) ? 4 : 3;
707
708             if (suppressAlpha) {
709                 postProcess = POST_REMOVE_RGB_TRANS;
710             } else if (performGammaCorrection) {
711                 postProcess = POST_GAMMA;
712             } else {
713                 postProcess = POST_NONE;
714             }
715             break;
716         }
717     }
718
719     private void parse_IEND_chunk(PNGChunk chunk) throws Exception JavaDoc {
720         // Store text strings
721
int textLen = textKeys.size();
722         String JavaDoc[] textArray = new String JavaDoc[2*textLen];
723         for (int i = 0; i < textLen; i++) {
724             String JavaDoc key = (String JavaDoc)textKeys.elementAt(i);
725             String JavaDoc val = (String JavaDoc)textStrings.elementAt(i);
726             textArray[2*i] = key;
727             textArray[2*i + 1] = val;
728             if (emitProperties) {
729                 String JavaDoc uniqueKey = "text_" + i + ":" + key;
730                 properties.put(uniqueKey.toLowerCase(), val);
731             }
732         }
733         if (encodeParam != null) {
734             encodeParam.setText(textArray);
735         }
736
737         // Store compressed text strings
738
int ztextLen = ztextKeys.size();
739         String JavaDoc[] ztextArray = new String JavaDoc[2*ztextLen];
740         for (int i = 0; i < ztextLen; i++) {
741             String JavaDoc key = (String JavaDoc)ztextKeys.elementAt(i);
742             String JavaDoc val = (String JavaDoc)ztextStrings.elementAt(i);
743             ztextArray[2*i] = key;
744             ztextArray[2*i + 1] = val;
745             if (emitProperties) {
746                 String JavaDoc uniqueKey = "ztext_" + i + ":" + key;
747                 properties.put(uniqueKey.toLowerCase(), val);
748             }
749         }
750         if (encodeParam != null) {
751             encodeParam.setCompressedText(ztextArray);
752         }
753
754         // Parse prior IDAT chunks
755
InputStream JavaDoc seqStream =
756             new SequenceInputStream JavaDoc(streamVec.elements());
757         InputStream JavaDoc infStream =
758             new InflaterInputStream JavaDoc(seqStream, new Inflater JavaDoc());
759         dataStream = new DataInputStream JavaDoc(infStream);
760         
761         // Create an empty WritableRaster
762
int depth = bitDepth;
763         if ((colorType == PNG_COLOR_GRAY) &&
764             (bitDepth < 8) && output8BitGray) {
765             depth = 8;
766         }
767         if ((colorType == PNG_COLOR_PALETTE) && expandPalette) {
768             depth = 8;
769         }
770         int bytesPerRow = (outputBands*width*depth + 7)/8;
771         int scanlineStride =
772             (depth == 16) ? (bytesPerRow/2) : bytesPerRow;
773
774         theTile = createRaster(width, height, outputBands,
775                                scanlineStride,
776                                depth);
777
778         if (performGammaCorrection && (gammaLut == null)) {
779             initGammaLut(bitDepth);
780         }
781         if ((postProcess == POST_GRAY_LUT) ||
782             (postProcess == POST_GRAY_LUT_ADD_TRANS) ||
783             (postProcess == POST_GRAY_LUT_ADD_TRANS_EXP)) {
784             initGrayLut(bitDepth);
785         }
786
787         decodeImage(interlaceMethod == 1);
788         sampleModel = theTile.getSampleModel();
789
790         if ((colorType == PNG_COLOR_PALETTE) && !expandPalette) {
791             if (outputHasAlphaPalette) {
792                 colorModel = new IndexColorModel JavaDoc(bitDepth,
793                                                  paletteEntries,
794                                                  redPalette,
795                                                  greenPalette,
796                                                  bluePalette,
797                                                  alphaPalette);
798             } else {
799                 colorModel = new IndexColorModel JavaDoc(bitDepth,
800                                                  paletteEntries,
801                                                  redPalette,
802                                                  greenPalette,
803                                                  bluePalette);
804             }
805         } else if ((colorType == PNG_COLOR_GRAY) &&
806                    (bitDepth < 8) && !output8BitGray) {
807             byte[] palette = expandBits[bitDepth];
808             colorModel = new IndexColorModel JavaDoc(bitDepth,
809                                              palette.length,
810                                              palette,
811                                              palette,
812                                              palette);
813         } else {
814             colorModel =
815                 createComponentColorModel(sampleModel);
816         }
817     }
818
819     private static final int[] GrayBits8 = { 8 };
820     private static final ComponentColorModel JavaDoc colorModelGray8 =
821         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
822                                 GrayBits8, false, false,
823                                 Transparency.OPAQUE,
824                                 DataBuffer.TYPE_BYTE);
825
826     private static final int[] GrayAlphaBits8 = { 8, 8 };
827     private static final ComponentColorModel JavaDoc colorModelGrayAlpha8 =
828         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
829                                 GrayAlphaBits8, true, false,
830                                 Transparency.TRANSLUCENT,
831                                 DataBuffer.TYPE_BYTE);
832
833     private static final int[] GrayBits16 = { 16 };
834     private static final ComponentColorModel JavaDoc colorModelGray16 =
835         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
836                                 GrayBits16, false, false,
837                                 Transparency.OPAQUE,
838                                 DataBuffer.TYPE_USHORT);
839
840     private static final int[] GrayAlphaBits16 = { 16, 16 };
841     private static final ComponentColorModel JavaDoc colorModelGrayAlpha16 =
842         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
843                                 GrayAlphaBits16, true, false,
844                                 Transparency.TRANSLUCENT,
845                                 DataBuffer.TYPE_USHORT);
846
847     private static final int[] GrayBits32 = { 32 };
848     private static final ComponentColorModel JavaDoc colorModelGray32 =
849         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
850                                 GrayBits32, false, false,
851                                 Transparency.OPAQUE,
852                                 DataBuffer.TYPE_INT);
853
854     private static final int[] GrayAlphaBits32 = { 32, 32 };
855     private static final ComponentColorModel JavaDoc colorModelGrayAlpha32 =
856         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
857                                 GrayAlphaBits32, true, false,
858                                 Transparency.TRANSLUCENT,
859                                 DataBuffer.TYPE_INT);
860
861     private static final int[] RGBBits8 = { 8, 8, 8 };
862     private static final ComponentColorModel JavaDoc colorModelRGB8 =
863       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
864                               RGBBits8, false, false,
865                               Transparency.OPAQUE,
866                               DataBuffer.TYPE_BYTE);
867
868     private static final int[] RGBABits8 = { 8, 8, 8, 8 };
869     private static final ComponentColorModel JavaDoc colorModelRGBA8 =
870       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
871                               RGBABits8, true, false,
872                               Transparency.TRANSLUCENT,
873                               DataBuffer.TYPE_BYTE);
874
875     private static final int[] RGBBits16 = { 16, 16, 16 };
876     private static final ComponentColorModel JavaDoc colorModelRGB16 =
877       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
878                               RGBBits16, false, false,
879                               Transparency.OPAQUE,
880                               DataBuffer.TYPE_USHORT);
881
882     private static final int[] RGBABits16 = { 16, 16, 16, 16 };
883     private static final ComponentColorModel JavaDoc colorModelRGBA16 =
884       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
885                               RGBABits16, true, false,
886                               Transparency.TRANSLUCENT,
887                               DataBuffer.TYPE_USHORT);
888
889     private static final int[] RGBBits32 = { 32, 32, 32 };
890     private static final ComponentColorModel JavaDoc colorModelRGB32 =
891       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
892                               RGBBits32, false, false,
893                               Transparency.OPAQUE,
894                               DataBuffer.TYPE_INT);
895
896     private static final int[] RGBABits32 = { 32, 32, 32, 32 };
897     private static final ComponentColorModel JavaDoc colorModelRGBA32 =
898       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
899                               RGBABits32, true, false,
900                               Transparency.TRANSLUCENT,
901                               DataBuffer.TYPE_INT);
902     /**
903      * A convenience method to create an instance of
904      * <code>ComponentColorModel</code> suitable for use with the
905      * given <code>SampleModel</code>. The <code>SampleModel</code>
906      * should have a data type of <code>DataBuffer.TYPE_BYTE</code>,
907      * <code>TYPE_USHORT</code>, or <code>TYPE_INT</code> and between
908      * 1 and 4 bands. Depending on the number of bands of the
909      * <code>SampleModel</code>, either a gray, gray+alpha, rgb, or
910      * rgb+alpha <code>ColorModel</code> is returned.
911      */

912     public static ColorModel JavaDoc createComponentColorModel(SampleModel JavaDoc sm) {
913         int type = sm.getDataType();
914         int bands = sm.getNumBands();
915         ComponentColorModel JavaDoc cm = null;
916
917         if (type == DataBuffer.TYPE_BYTE) {
918             switch (bands) {
919             case 1:
920                 cm = colorModelGray8;
921                 break;
922             case 2:
923                 cm = colorModelGrayAlpha8;
924                 break;
925             case 3:
926                 cm = colorModelRGB8;
927                 break;
928             case 4:
929                 cm = colorModelRGBA8;
930                 break;
931             }
932         } else if (type == DataBuffer.TYPE_USHORT) {
933             switch (bands) {
934             case 1:
935                 cm = colorModelGray16;
936                 break;
937             case 2:
938                 cm = colorModelGrayAlpha16;
939                 break;
940             case 3:
941                 cm = colorModelRGB16;
942                 break;
943             case 4:
944                 cm = colorModelRGBA16;
945                 break;
946             }
947         } else if (type == DataBuffer.TYPE_INT) {
948             switch (bands) {
949             case 1:
950                 cm = colorModelGray32;
951                 break;
952             case 2:
953                 cm = colorModelGrayAlpha32;
954                 break;
955             case 3:
956                 cm = colorModelRGB32;
957                 break;
958             case 4:
959                 cm = colorModelRGBA32;
960                 break;
961             }
962         }
963
964         return cm;
965     }
966
967     private void parse_PLTE_chunk(PNGChunk chunk) {
968         paletteEntries = chunk.getLength()/3;
969         redPalette = new byte[paletteEntries];
970         greenPalette = new byte[paletteEntries];
971         bluePalette = new byte[paletteEntries];
972
973         int pltIndex = 0;
974
975         // gAMA chunk must precede PLTE chunk
976
if (performGammaCorrection) {
977             if (gammaLut == null) {
978                 initGammaLut(bitDepth == 16 ? 16 : 8);
979             }
980             
981             for (int i = 0; i < paletteEntries; i++) {
982                 byte r = chunk.getByte(pltIndex++);
983                 byte g = chunk.getByte(pltIndex++);
984                 byte b = chunk.getByte(pltIndex++);
985
986                 redPalette[i] = (byte)gammaLut[r & 0xff];
987                 greenPalette[i] = (byte)gammaLut[g & 0xff];
988                 bluePalette[i] = (byte)gammaLut[b & 0xff];
989             }
990         } else {
991             for (int i = 0; i < paletteEntries; i++) {
992                 redPalette[i] = chunk.getByte(pltIndex++);
993                 greenPalette[i] = chunk.getByte(pltIndex++);
994                 bluePalette[i] = chunk.getByte(pltIndex++);
995             }
996         }
997     }
998
999     private void parse_bKGD_chunk(PNGChunk chunk) {
1000        hasBackground = true;
1001
1002        switch (colorType) {
1003        case PNG_COLOR_PALETTE:
1004            int bkgdIndex = chunk.getByte(0) & 0xff;
1005
1006            bkgdRed = redPalette[bkgdIndex] & 0xff;
1007            bkgdGreen = greenPalette[bkgdIndex] & 0xff;
1008            bkgdBlue = bluePalette[bkgdIndex] & 0xff;
1009
1010            if (encodeParam != null) {
1011                ((PNGEncodeParam.Palette)encodeParam).
1012                    setBackgroundPaletteIndex(bkgdIndex);
1013            }
1014            break;
1015        case PNG_COLOR_GRAY: case PNG_COLOR_GRAY_ALPHA:
1016            int bkgdGray = chunk.getInt2(0);
1017            bkgdRed = bkgdGreen = bkgdBlue = bkgdGray;
1018
1019            if (encodeParam != null) {
1020                ((PNGEncodeParam.Gray)encodeParam).
1021                    setBackgroundGray(bkgdGray);
1022            }
1023            break;
1024        case PNG_COLOR_RGB: case PNG_COLOR_RGB_ALPHA:
1025            bkgdRed = chunk.getInt2(0);
1026            bkgdGreen = chunk.getInt2(2);
1027            bkgdBlue = chunk.getInt2(4);
1028
1029            int[] bkgdRGB = new int[3];
1030            bkgdRGB[0] = bkgdRed;
1031            bkgdRGB[1] = bkgdGreen;
1032            bkgdRGB[2] = bkgdBlue;
1033            if (encodeParam != null) {
1034                ((PNGEncodeParam.RGB)encodeParam).
1035                    setBackgroundRGB(bkgdRGB);
1036            }
1037            break;
1038        }
1039
1040        int r = 0, g = 0, b = 0;
1041        if (bitDepth < 8) {
1042            r = expandBits[bitDepth][bkgdRed];
1043            g = expandBits[bitDepth][bkgdGreen];
1044            b = expandBits[bitDepth][bkgdBlue];
1045        } else if (bitDepth == 8) {
1046            r = bkgdRed;
1047            g = bkgdGreen;
1048            b = bkgdBlue;
1049        } else if (bitDepth == 16) {
1050            r = bkgdRed >> 8;
1051            g = bkgdGreen >> 8;
1052            b = bkgdBlue >> 8;
1053        }
1054        if (emitProperties) {
1055            properties.put("background_color", new Color JavaDoc(r, g, b));
1056        }
1057    }
1058
1059    private void parse_cHRM_chunk(PNGChunk chunk) {
1060        // If an sRGB chunk exists, ignore cHRM chunks
1061
if (sRGBRenderingIntent != -1) {
1062            return;
1063        }
1064
1065        chromaticity = new float[8];
1066        chromaticity[0] = chunk.getInt4(0)/100000.0F;
1067        chromaticity[1] = chunk.getInt4(4)/100000.0F;
1068        chromaticity[2] = chunk.getInt4(8)/100000.0F;
1069        chromaticity[3] = chunk.getInt4(12)/100000.0F;
1070        chromaticity[4] = chunk.getInt4(16)/100000.0F;
1071        chromaticity[5] = chunk.getInt4(20)/100000.0F;
1072        chromaticity[6] = chunk.getInt4(24)/100000.0F;
1073        chromaticity[7] = chunk.getInt4(28)/100000.0F;
1074
1075        if (encodeParam != null) {
1076            encodeParam.setChromaticity(chromaticity);
1077        }
1078        if (emitProperties) {
1079            properties.put("white_point_x", new Float JavaDoc(chromaticity[0]));
1080            properties.put("white_point_y", new Float JavaDoc(chromaticity[1]));
1081            properties.put("red_x", new Float JavaDoc(chromaticity[2]));
1082            properties.put("red_y", new Float JavaDoc(chromaticity[3]));
1083            properties.put("green_x", new Float JavaDoc(chromaticity[4]));
1084            properties.put("green_y", new Float JavaDoc(chromaticity[5]));
1085            properties.put("blue_x", new Float JavaDoc(chromaticity[6]));
1086            properties.put("blue_y", new Float JavaDoc(chromaticity[7]));
1087        }
1088    }
1089
1090    private void parse_gAMA_chunk(PNGChunk chunk) {
1091        // If an sRGB chunk exists, ignore gAMA chunks
1092
if (sRGBRenderingIntent != -1) {
1093            return;
1094        }
1095
1096        fileGamma = chunk.getInt4(0)/100000.0F;
1097
1098        float exp =
1099            performGammaCorrection ? displayExponent/userExponent : 1.0F;
1100        if (encodeParam != null) {
1101            encodeParam.setGamma(fileGamma*exp);
1102        }
1103        if (emitProperties) {
1104            properties.put("gamma", new Float JavaDoc(fileGamma*exp));
1105        }
1106    }
1107    
1108    private void parse_hIST_chunk(PNGChunk chunk) {
1109        if (redPalette == null) {
1110            String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder18");
1111            throw new RuntimeException JavaDoc(msg);
1112        }
1113
1114        int length = redPalette.length;
1115        int[] hist = new int[length];
1116        for (int i = 0; i < length; i++) {
1117            hist[i] = chunk.getInt2(2*i);
1118        }
1119        
1120        if (encodeParam != null) {
1121            encodeParam.setPaletteHistogram(hist);
1122        }
1123    }
1124
1125    private void parse_iCCP_chunk(PNGChunk chunk) {
1126        String JavaDoc name = new String JavaDoc();
1127        byte b;
1128
1129        int textIndex = 0;
1130        while ((b = chunk.getByte(textIndex++)) != 0) {
1131            name += (char)b;
1132        }
1133    }
1134
1135    private void parse_pHYs_chunk(PNGChunk chunk) {
1136        int xPixelsPerUnit = chunk.getInt4(0);
1137        int yPixelsPerUnit = chunk.getInt4(4);
1138        int unitSpecifier = chunk.getInt1(8);
1139
1140        if (encodeParam != null) {
1141            encodeParam.setPhysicalDimension(xPixelsPerUnit,
1142                                             yPixelsPerUnit,
1143                                             unitSpecifier);
1144        }
1145        if (emitProperties) {
1146            properties.put("x_pixels_per_unit", new Integer JavaDoc(xPixelsPerUnit));
1147            properties.put("y_pixels_per_unit", new Integer JavaDoc(yPixelsPerUnit));
1148            properties.put("pixel_aspect_ratio",
1149                           new Float JavaDoc((float)xPixelsPerUnit/yPixelsPerUnit));
1150            if (unitSpecifier == 1) {
1151                properties.put("pixel_units", "Meters");
1152            } else if (unitSpecifier != 0) {
1153                // Error -- unit specifier must be 0 or 1
1154
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder12");
1155                throw new RuntimeException JavaDoc(msg);
1156            }
1157        }
1158    }
1159
1160    private void parse_sBIT_chunk(PNGChunk chunk) {
1161        if (colorType == PNG_COLOR_PALETTE) {
1162            significantBits = new int[3];
1163        } else {
1164            significantBits = new int[inputBands];
1165        }
1166        for (int i = 0; i < significantBits.length; i++) {
1167            int bits = chunk.getByte(i);
1168            int depth = (colorType == PNG_COLOR_PALETTE) ? 8 : bitDepth;
1169            if (bits <= 0 || bits > depth) {
1170                // Error -- significant bits must be between 0 and
1171
// image bit depth.
1172
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder13");
1173                throw new RuntimeException JavaDoc(msg);
1174            }
1175            significantBits[i] = bits;
1176        }
1177
1178        if (encodeParam != null) {
1179            encodeParam.setSignificantBits(significantBits);
1180        }
1181        if (emitProperties) {
1182            properties.put("significant_bits", significantBits);
1183        }
1184    }
1185
1186    private void parse_sRGB_chunk(PNGChunk chunk) {
1187        sRGBRenderingIntent = chunk.getByte(0);
1188
1189        // The presence of an sRGB chunk implies particular
1190
// settings for gamma and chroma.
1191
fileGamma = 45455/100000.0F;
1192
1193        chromaticity = new float[8];
1194        chromaticity[0] = 31270/10000.0F;
1195        chromaticity[1] = 32900/10000.0F;
1196        chromaticity[2] = 64000/10000.0F;
1197        chromaticity[3] = 33000/10000.0F;
1198        chromaticity[4] = 30000/10000.0F;
1199        chromaticity[5] = 60000/10000.0F;
1200        chromaticity[6] = 15000/10000.0F;
1201        chromaticity[7] = 6000/10000.0F;
1202
1203        if (performGammaCorrection) {
1204            // File gamma is 1/2.2
1205
float gamma = fileGamma*(displayExponent/userExponent);
1206            if (encodeParam != null) {
1207                encodeParam.setGamma(gamma);
1208                encodeParam.setChromaticity(chromaticity);
1209            }
1210            if (emitProperties) {
1211                properties.put("gamma", new Float JavaDoc(gamma));
1212                properties.put("white_point_x", new Float JavaDoc(chromaticity[0]));
1213                properties.put("white_point_y", new Float JavaDoc(chromaticity[1]));
1214                properties.put("red_x", new Float JavaDoc(chromaticity[2]));
1215                properties.put("red_y", new Float JavaDoc(chromaticity[3]));
1216                properties.put("green_x", new Float JavaDoc(chromaticity[4]));
1217                properties.put("green_y", new Float JavaDoc(chromaticity[5]));
1218                properties.put("blue_x", new Float JavaDoc(chromaticity[6]));
1219                properties.put("blue_y", new Float JavaDoc(chromaticity[7]));
1220            }
1221        }
1222    }
1223
1224    private void parse_tEXt_chunk(PNGChunk chunk) {
1225        String JavaDoc key = new String JavaDoc();
1226        String JavaDoc value = new String JavaDoc();
1227        byte b;
1228        
1229        int textIndex = 0;
1230        while ((b = chunk.getByte(textIndex++)) != 0) {
1231            key += (char)b;
1232        }
1233
1234        for (int i = textIndex; i < chunk.getLength(); i++) {
1235            value += (char)chunk.getByte(i);
1236        }
1237
1238        textKeys.add(key);
1239        textStrings.add(value);
1240    }
1241
1242    private void parse_tIME_chunk(PNGChunk chunk) {
1243        int year = chunk.getInt2(0);
1244        int month = chunk.getInt1(2) - 1;
1245        int day = chunk.getInt1(3);
1246        int hour = chunk.getInt1(4);
1247        int minute = chunk.getInt1(5);
1248        int second = chunk.getInt1(6);
1249        
1250        TimeZone JavaDoc gmt = TimeZone.getTimeZone("GMT");
1251        
1252        GregorianCalendar JavaDoc cal = new GregorianCalendar JavaDoc(gmt);
1253        cal.set(year, month, day,
1254                hour, minute, second);
1255        Date JavaDoc date = cal.getTime();
1256        
1257        if (encodeParam != null) {
1258            encodeParam.setModificationTime(date);
1259        }
1260        if (emitProperties) {
1261            properties.put("timestamp", date);
1262        }
1263    }
1264
1265    private void parse_tRNS_chunk(PNGChunk chunk) {
1266        if (colorType == PNG_COLOR_PALETTE) {
1267            int entries = chunk.getLength();
1268            if (entries > paletteEntries) {
1269                // Error -- mustn't have more alpha than RGB palette entries
1270
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder14");
1271                throw new RuntimeException JavaDoc(msg);
1272            }
1273
1274            // Load beginning of palette from the chunk
1275
alphaPalette = new byte[paletteEntries];
1276            for (int i = 0; i < entries; i++) {
1277                alphaPalette[i] = chunk.getByte(i);
1278            }
1279            
1280            // Fill rest of palette with 255
1281
for (int i = entries; i < paletteEntries; i++) {
1282                alphaPalette[i] = (byte)255;
1283            }
1284
1285            if (!suppressAlpha) {
1286                if (expandPalette) {
1287                    postProcess = POST_PALETTE_TO_RGBA;
1288                    outputBands = 4;
1289                } else {
1290                    outputHasAlphaPalette = true;
1291                }
1292            }
1293        } else if (colorType == PNG_COLOR_GRAY) {
1294            grayTransparentAlpha = chunk.getInt2(0);
1295            
1296            if (!suppressAlpha) {
1297                if (bitDepth < 8) {
1298                    output8BitGray = true;
1299                    maxOpacity = 255;
1300                    postProcess = POST_GRAY_LUT_ADD_TRANS;
1301                } else {
1302                    postProcess = POST_ADD_GRAY_TRANS;
1303                }
1304
1305                if (expandGrayAlpha) {
1306                    outputBands = 4;
1307                    postProcess |= POST_EXP_MASK;
1308                } else {
1309                    outputBands = 2;
1310                }
1311                
1312                if (encodeParam != null) {
1313                    ((PNGEncodeParam.Gray)encodeParam).
1314                        setTransparentGray(grayTransparentAlpha);
1315                }
1316            }
1317        } else if (colorType == PNG_COLOR_RGB) {
1318            redTransparentAlpha = chunk.getInt2(0);
1319            greenTransparentAlpha = chunk.getInt2(2);
1320            blueTransparentAlpha = chunk.getInt2(4);
1321
1322            if (!suppressAlpha) {
1323                outputBands = 4;
1324                postProcess = POST_ADD_RGB_TRANS;
1325                
1326                if (encodeParam != null) {
1327                    int[] rgbTrans = new int[3];
1328                    rgbTrans[0] = redTransparentAlpha;
1329                    rgbTrans[1] = greenTransparentAlpha;
1330                    rgbTrans[2] = blueTransparentAlpha;
1331                    ((PNGEncodeParam.RGB)encodeParam).
1332                        setTransparentRGB(rgbTrans);
1333                }
1334            }
1335        } else if (colorType == PNG_COLOR_GRAY_ALPHA ||
1336                   colorType == PNG_COLOR_RGB_ALPHA) {
1337            // Error -- GA or RGBA image can't have a tRNS chunk.
1338
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder15");
1339            throw new RuntimeException JavaDoc(msg);
1340        }
1341    }
1342
1343    private void parse_zTXt_chunk(PNGChunk chunk) {
1344        String JavaDoc key = new String JavaDoc();
1345        String JavaDoc value = new String JavaDoc();
1346        byte b;
1347        
1348        int textIndex = 0;
1349        while ((b = chunk.getByte(textIndex++)) != 0) {
1350            key += (char)b;
1351        }
1352        /* int method = */ chunk.getByte(textIndex++);
1353
1354        try {
1355            int length = chunk.getLength() - textIndex;
1356            byte[] data = chunk.getData();
1357            InputStream JavaDoc cis =
1358                new ByteArrayInputStream JavaDoc(data, textIndex, length);
1359            InputStream JavaDoc iis = new InflaterInputStream JavaDoc(cis);
1360            
1361            int c;
1362            while ((c = iis.read()) != -1) {
1363                value += (char)c;
1364            }
1365            
1366            ztextKeys.add(key);
1367            ztextStrings.add(value);
1368        } catch (Exception JavaDoc e) {
1369            e.printStackTrace();
1370        }
1371    }
1372
1373    private WritableRaster JavaDoc createRaster(int width, int height, int bands,
1374                                        int scanlineStride,
1375                                        int bitDepth) {
1376
1377        DataBuffer JavaDoc dataBuffer;
1378        WritableRaster JavaDoc ras = null;
1379        Point JavaDoc origin = new Point JavaDoc(0, 0);
1380        if ((bitDepth < 8) && (bands == 1)) {
1381            dataBuffer = new DataBufferByte JavaDoc(height*scanlineStride);
1382            ras = Raster.createPackedRaster(dataBuffer,
1383                                            width, height,
1384                                            bitDepth,
1385                                            origin);
1386        } else if (bitDepth <= 8) {
1387            dataBuffer = new DataBufferByte JavaDoc(height*scanlineStride);
1388           ras = Raster.createInterleavedRaster(dataBuffer,
1389                                                 width, height,
1390                                                 scanlineStride,
1391                                                 bands,
1392                                                 bandOffsets[bands],
1393                                                 origin);
1394        } else {
1395            dataBuffer = new DataBufferUShort JavaDoc(height*scanlineStride);
1396            ras = Raster.createInterleavedRaster(dataBuffer,
1397                                                 width, height,
1398                                                 scanlineStride,
1399                                                 bands,
1400                                                 bandOffsets[bands],
1401                                                 origin);
1402        }
1403
1404        return ras;
1405    }
1406
1407    // Data filtering methods
1408

1409    private static void decodeSubFilter(byte[] curr, int count, int bpp) {
1410        for (int i = bpp; i < count; i++) {
1411            int val;
1412
1413            val = curr[i] & 0xff;
1414            val += curr[i - bpp] & 0xff;
1415
1416            curr[i] = (byte)val;
1417        }
1418    }
1419
1420    private static void decodeUpFilter(byte[] curr, byte[] prev,
1421                                       int count) {
1422        for (int i = 0; i < count; i++) {
1423            int raw = curr[i] & 0xff;
1424            int prior = prev[i] & 0xff;
1425
1426            curr[i] = (byte)(raw + prior);
1427        }
1428    }
1429
1430    private static void decodeAverageFilter(byte[] curr, byte[] prev,
1431                                            int count, int bpp) {
1432        int raw, priorPixel, priorRow;
1433
1434        for (int i = 0; i < bpp; i++) {
1435            raw = curr[i] & 0xff;
1436            priorRow = prev[i] & 0xff;
1437            
1438            curr[i] = (byte)(raw + priorRow/2);
1439        }
1440
1441        for (int i = bpp; i < count; i++) {
1442            raw = curr[i] & 0xff;
1443            priorPixel = curr[i - bpp] & 0xff;
1444            priorRow = prev[i] & 0xff;
1445            
1446            curr[i] = (byte)(raw + (priorPixel + priorRow)/2);
1447        }
1448    }
1449
1450    private static int paethPredictor(int a, int b, int c) {
1451        int p = a + b - c;
1452        int pa = Math.abs(p - a);
1453        int pb = Math.abs(p - b);
1454        int pc = Math.abs(p - c);
1455
1456        if ((pa <= pb) && (pa <= pc)) {
1457            return a;
1458        } else if (pb <= pc) {
1459            return b;
1460        } else {
1461            return c;
1462        }
1463    }
1464
1465    private static void decodePaethFilter(byte[] curr, byte[] prev,
1466                                          int count, int bpp) {
1467        int raw, priorPixel, priorRow, priorRowPixel;
1468
1469        for (int i = 0; i < bpp; i++) {
1470            raw = curr[i] & 0xff;
1471            priorRow = prev[i] & 0xff;
1472
1473            curr[i] = (byte)(raw + priorRow);
1474        }
1475
1476        for (int i = bpp; i < count; i++) {
1477            raw = curr[i] & 0xff;
1478            priorPixel = curr[i - bpp] & 0xff;
1479            priorRow = prev[i] & 0xff;
1480            priorRowPixel = prev[i - bpp] & 0xff;
1481
1482            curr[i] = (byte)(raw + paethPredictor(priorPixel,
1483                                                  priorRow,
1484                                                  priorRowPixel));
1485        }
1486    }
1487
1488    private void processPixels(int process,
1489                               Raster JavaDoc src, WritableRaster JavaDoc dst,
1490                               int xOffset, int step, int y, int width) {
1491        int srcX, dstX;
1492
1493        // Create an array suitable for holding one pixel
1494
int[] ps = src.getPixel(0, 0, (int[])null);
1495        int[] pd = dst.getPixel(0, 0, (int[])null);
1496
1497        dstX = xOffset;
1498        switch (process) {
1499        case POST_NONE:
1500            for (srcX = 0; srcX < width; srcX++) {
1501                src.getPixel(srcX, 0, ps);
1502                dst.setPixel(dstX, y, ps);
1503                dstX += step;
1504            }
1505            break;
1506
1507        case POST_GAMMA:
1508            for (srcX = 0; srcX < width; srcX++) {
1509                src.getPixel(srcX, 0, ps);
1510                    
1511                for (int i = 0; i < inputBands; i++) {
1512                    int x = ps[i];
1513                    ps[i] = gammaLut[x];
1514                }
1515                    
1516                dst.setPixel(dstX, y, ps);
1517                dstX += step;
1518            }
1519            break;
1520
1521        case POST_GRAY_LUT:
1522            for (srcX = 0; srcX < width; srcX++) {
1523                src.getPixel(srcX, 0, ps);
1524
1525                pd[0] = grayLut[ps[0]];
1526
1527                dst.setPixel(dstX, y, pd);
1528                dstX += step;
1529            }
1530            break;
1531
1532        case POST_GRAY_LUT_ADD_TRANS:
1533            for (srcX = 0; srcX < width; srcX++) {
1534                src.getPixel(srcX, 0, ps);
1535
1536                int val = ps[0];
1537                pd[0] = grayLut[val];
1538                if (val == grayTransparentAlpha) {
1539                    pd[1] = 0;
1540                } else {
1541                    pd[1] = maxOpacity;
1542                }
1543                
1544                dst.setPixel(dstX, y, pd);
1545                dstX += step;
1546            }
1547            break;
1548
1549        case POST_PALETTE_TO_RGB:
1550            for (srcX = 0; srcX < width; srcX++) {
1551                src.getPixel(srcX, 0, ps);
1552                
1553                int val = ps[0];
1554                pd[0] = redPalette[val];
1555                pd[1] = greenPalette[val];
1556                pd[2] = bluePalette[val];
1557                
1558                dst.setPixel(dstX, y, pd);
1559                dstX += step;
1560            }
1561            break;
1562
1563        case POST_PALETTE_TO_RGBA:
1564            for (srcX = 0; srcX < width; srcX++) {
1565                src.getPixel(srcX, 0, ps);
1566
1567                int val = ps[0];
1568                pd[0] = redPalette[val];
1569                pd[1] = greenPalette[val];
1570                pd[2] = bluePalette[val];
1571                pd[3] = alphaPalette[val];
1572
1573                dst.setPixel(dstX, y, pd);
1574                dstX += step;
1575            }
1576            break;
1577
1578        case POST_ADD_GRAY_TRANS:
1579            for (srcX = 0; srcX < width; srcX++) {
1580                src.getPixel(srcX, 0, ps);
1581
1582                int val = ps[0];
1583                if (performGammaCorrection) {
1584                    val = gammaLut[val];
1585                }
1586                pd[0] = val;
1587                if (val == grayTransparentAlpha) {
1588                    pd[1] = 0;
1589                } else {
1590                    pd[1] = maxOpacity;
1591                }
1592
1593                dst.setPixel(dstX, y, pd);
1594                dstX += step;
1595            }
1596            break;
1597
1598        case POST_ADD_RGB_TRANS:
1599            for (srcX = 0; srcX < width; srcX++) {
1600                src.getPixel(srcX, 0, ps);
1601
1602                int r = ps[0];
1603                int g = ps[1];
1604                int b = ps[2];
1605                if (performGammaCorrection) {
1606                    pd[0] = gammaLut[r];
1607                    pd[1] = gammaLut[g];
1608                    pd[2] = gammaLut[b];
1609                } else {
1610                    pd[0] = r;
1611                    pd[1] = g;
1612                    pd[2] = b;
1613                }
1614                if ((r == redTransparentAlpha) &&
1615                    (g == greenTransparentAlpha) &&
1616                    (b == blueTransparentAlpha)) {
1617                    pd[3] = 0;
1618                } else {
1619                    pd[3] = maxOpacity;
1620                }
1621
1622                dst.setPixel(dstX, y, pd);
1623                dstX += step;
1624            }
1625            break;
1626
1627        case POST_REMOVE_GRAY_TRANS:
1628            for (srcX = 0; srcX < width; srcX++) {
1629                src.getPixel(srcX, 0, ps);
1630
1631                int g = ps[0];
1632                if (performGammaCorrection) {
1633                    pd[0] = gammaLut[g];
1634                } else {
1635                    pd[0] = g;
1636                }
1637
1638                dst.setPixel(dstX, y, pd);
1639                dstX += step;
1640            }
1641            break;
1642
1643        case POST_REMOVE_RGB_TRANS:
1644            for (srcX = 0; srcX < width; srcX++) {
1645                src.getPixel(srcX, 0, ps);
1646
1647                int r = ps[0];
1648                int g = ps[1];
1649                int b = ps[2];
1650                if (performGammaCorrection) {
1651                    pd[0] = gammaLut[r];
1652                    pd[1] = gammaLut[g];
1653                    pd[2] = gammaLut[b];
1654                } else {
1655                    pd[0] = r;
1656                    pd[1] = g;
1657                    pd[2] = b;
1658                }
1659
1660                dst.setPixel(dstX, y, pd);
1661                dstX += step;
1662            }
1663            break;
1664
1665        case POST_GAMMA_EXP:
1666            for (srcX = 0; srcX < width; srcX++) {
1667                src.getPixel(srcX, 0, ps);
1668                    
1669                int val = ps[0];
1670                int alpha = ps[1];
1671                int gamma = gammaLut[val];
1672                pd[0] = gamma;
1673                pd[1] = gamma;
1674                pd[2] = gamma;
1675                pd[3] = alpha;
1676                    
1677                dst.setPixel(dstX, y, pd);
1678                dstX += step;
1679            }
1680            break;
1681
1682        case POST_GRAY_ALPHA_EXP:
1683            for (srcX = 0; srcX < width; srcX++) {
1684                src.getPixel(srcX, 0, ps);
1685                    
1686                int val = ps[0];
1687                int alpha = ps[1];
1688                pd[0] = val;
1689                pd[1] = val;
1690                pd[2] = val;
1691                pd[3] = alpha;
1692                    
1693                dst.setPixel(dstX, y, pd);
1694                dstX += step;
1695            }
1696            break;
1697
1698        case POST_ADD_GRAY_TRANS_EXP:
1699            for (srcX = 0; srcX < width; srcX++) {
1700                src.getPixel(srcX, 0, ps);
1701
1702                int val = ps[0];
1703                if (performGammaCorrection) {
1704                    val = gammaLut[val];
1705                }
1706                pd[0] = val;
1707                pd[1] = val;
1708                pd[2] = val;
1709                if (val == grayTransparentAlpha) {
1710                    pd[3] = 0;
1711                } else {
1712                    pd[3] = maxOpacity;
1713                }
1714
1715                dst.setPixel(dstX, y, pd);
1716                dstX += step;
1717            }
1718            break;
1719
1720        case POST_GRAY_LUT_ADD_TRANS_EXP:
1721            for (srcX = 0; srcX < width; srcX++) {
1722                src.getPixel(srcX, 0, ps);
1723
1724                int val = ps[0];
1725                int val2 = grayLut[val];
1726                pd[0] = val2;
1727                pd[1] = val2;
1728                pd[2] = val2;
1729                if (val == grayTransparentAlpha) {
1730                    pd[3] = 0;
1731                } else {
1732                    pd[3] = maxOpacity;
1733                }
1734                
1735                dst.setPixel(dstX, y, pd);
1736                dstX += step;
1737            }
1738            break;
1739        }
1740    }
1741
1742    /**
1743     * Reads in an image of a given size and returns it as a
1744     * WritableRaster.
1745     */

1746    private void decodePass(WritableRaster JavaDoc imRas,
1747                            int xOffset, int yOffset,
1748                            int xStep, int yStep,
1749                            int passWidth, int passHeight) {
1750        if ((passWidth == 0) || (passHeight == 0)) {
1751            return;
1752        }
1753
1754        int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
1755        int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;
1756        byte[] curr = new byte[bytesPerRow];
1757        byte[] prior = new byte[bytesPerRow];
1758
1759        // Create a 1-row tall Raster to hold the data
1760
WritableRaster JavaDoc passRow =
1761            createRaster(passWidth, 1, inputBands,
1762                         eltsPerRow,
1763                         bitDepth);
1764        DataBuffer JavaDoc dataBuffer = passRow.getDataBuffer();
1765        int type = dataBuffer.getDataType();
1766        byte[] byteData = null;
1767        short[] shortData = null;
1768        if (type == DataBuffer.TYPE_BYTE) {
1769            byteData = ((DataBufferByte JavaDoc)dataBuffer).getData();
1770        } else {
1771            shortData = ((DataBufferUShort JavaDoc)dataBuffer).getData();
1772        }
1773
1774        // Decode the (sub)image row-by-row
1775
int srcY, dstY;
1776        for (srcY = 0, dstY = yOffset;
1777             srcY < passHeight;
1778             srcY++, dstY += yStep) {
1779            // Read the filter type byte and a row of data
1780
int filter = 0;
1781            try {
1782                filter = dataStream.read();
1783                dataStream.readFully(curr, 0, bytesPerRow);
1784            } catch (Exception JavaDoc e) {
1785                e.printStackTrace();
1786            }
1787
1788            switch (filter) {
1789            case PNG_FILTER_NONE:
1790                break;
1791            case PNG_FILTER_SUB:
1792                decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
1793                break;
1794            case PNG_FILTER_UP:
1795                decodeUpFilter(curr, prior, bytesPerRow);
1796                break;
1797            case PNG_FILTER_AVERAGE:
1798                decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
1799                break;
1800            case PNG_FILTER_PAETH:
1801                decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
1802                break;
1803            default:
1804                // Error -- uknown filter type
1805
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder16");
1806                throw new RuntimeException JavaDoc(msg);
1807            }
1808
1809            // Copy data into passRow byte by byte
1810
if (bitDepth < 16) {
1811                System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
1812            } else {
1813                int idx = 0;
1814                for (int j = 0; j < eltsPerRow; j++) {
1815                    shortData[j] =
1816                        (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
1817                    idx += 2;
1818                }
1819            }
1820
1821            processPixels(postProcess,
1822                          passRow, imRas, xOffset, xStep, dstY, passWidth);
1823
1824            // Swap curr and prior
1825
byte[] tmp = prior;
1826            prior = curr;
1827            curr = tmp;
1828        }
1829    }
1830
1831    private void decodeImage(boolean useInterlacing) {
1832        if (!useInterlacing) {
1833            decodePass(theTile, 0, 0, 1, 1, width, height);
1834        } else {
1835            decodePass(theTile, 0, 0, 8, 8, (width + 7)/8, (height + 7)/8);
1836            decodePass(theTile, 4, 0, 8, 8, (width + 3)/8, (height + 7)/8);
1837            decodePass(theTile, 0, 4, 4, 8, (width + 3)/4, (height + 3)/8);
1838            decodePass(theTile, 2, 0, 4, 4, (width + 1)/4, (height + 3)/4);
1839            decodePass(theTile, 0, 2, 2, 4, (width + 1)/2, (height + 1)/4);
1840            decodePass(theTile, 1, 0, 2, 2, width/2, (height + 1)/2);
1841            decodePass(theTile, 0, 1, 1, 2, width, height/2);
1842        }
1843    }
1844
1845    // RenderedImage stuff
1846

1847    public Raster JavaDoc getTile(int tileX, int tileY) {
1848        if (tileX != 0 || tileY != 0) {
1849            // Error -- bad tile requested
1850
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder17");
1851            throw new IllegalArgumentException JavaDoc(msg);
1852        }
1853        return theTile;
1854    }
1855}
1856
Popular Tags