KickJava   Java API By Example, From Geeks To Geeks.

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


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.Rectangle JavaDoc;
23 import java.awt.Transparency JavaDoc;
24 import java.awt.color.ColorSpace JavaDoc;
25 import java.awt.image.ColorModel JavaDoc;
26 import java.awt.image.ComponentColorModel JavaDoc;
27 import java.awt.image.DataBuffer JavaDoc;
28 import java.awt.image.DataBufferByte JavaDoc;
29 import java.awt.image.DataBufferUShort JavaDoc;
30 import java.awt.image.IndexColorModel JavaDoc;
31 import java.awt.image.Raster 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.Hashtable JavaDoc;
43 import java.util.TimeZone JavaDoc;
44 import java.util.Vector JavaDoc;
45 import java.util.zip.Inflater JavaDoc;
46 import java.util.zip.InflaterInputStream JavaDoc;
47
48 import org.apache.batik.ext.awt.image.GraphicsUtil;
49 import org.apache.batik.ext.awt.image.rendered.AbstractRed;
50 import org.apache.batik.ext.awt.image.rendered.CachableRed;
51
52 public class PNGRed extends AbstractRed {
53
54     static class PNGChunk {
55     int length;
56     int type;
57     byte[] data;
58     int crc;
59
60     String JavaDoc typeString;
61
62     public PNGChunk(int length, int type, byte[] data, int crc) {
63         this.length = length;
64         this.type = type;
65         this.data = data;
66         this.crc = crc;
67
68         typeString = new String JavaDoc();
69         typeString += (char)(type >> 24);
70         typeString += (char)((type >> 16) & 0xff);
71         typeString += (char)((type >> 8) & 0xff);
72         typeString += (char)(type & 0xff);
73     }
74
75     public int getLength() {
76         return length;
77     }
78
79     public int getType() {
80         return type;
81     }
82
83     public String JavaDoc getTypeString() {
84         return typeString;
85     }
86
87     public byte[] getData() {
88         return data;
89     }
90
91     public byte getByte(int offset) {
92         return data[offset];
93     }
94
95     public int getInt1(int offset) {
96         return data[offset] & 0xff;
97     }
98
99     public int getInt2(int offset) {
100         return ((data[offset] & 0xff) << 8) |
101         (data[offset + 1] & 0xff);
102     }
103
104     public int getInt4(int offset) {
105         return ((data[offset] & 0xff) << 24) |
106         ((data[offset + 1] & 0xff) << 16) |
107         ((data[offset + 2] & 0xff) << 8) |
108         (data[offset + 3] & 0xff);
109     }
110
111     public String JavaDoc getString4(int offset) {
112         String JavaDoc s = new String JavaDoc();
113         s += (char)data[offset];
114         s += (char)data[offset + 1];
115         s += (char)data[offset + 2];
116         s += (char)data[offset + 3];
117         return s;
118     }
119
120     public boolean isType(String JavaDoc typeName) {
121         return typeString.equals(typeName);
122     }
123     }
124
125     public static final int PNG_COLOR_GRAY = 0;
126     public static final int PNG_COLOR_RGB = 2;
127     public static final int PNG_COLOR_PALETTE = 3;
128     public static final int PNG_COLOR_GRAY_ALPHA = 4;
129     public static final int PNG_COLOR_RGB_ALPHA = 6;
130
131     private static final String JavaDoc[] colorTypeNames = {
132         "Grayscale", "Error", "Truecolor", "Index",
133         "Grayscale with alpha", "Error", "Truecolor with alpha"
134     };
135
136     public static final int PNG_FILTER_NONE = 0;
137     public static final int PNG_FILTER_SUB = 1;
138     public static final int PNG_FILTER_UP = 2;
139     public static final int PNG_FILTER_AVERAGE = 3;
140     public static final int PNG_FILTER_PAETH = 4;
141
142     private static final int RED_OFFSET = 2;
143     private static final int GREEN_OFFSET = 1;
144     private static final int BLUE_OFFSET = 0;
145
146     private int[][] bandOffsets = {
147         null,
148         { 0 }, // G
149
{ 0, 1 }, // GA in GA order
150
{ 0, 1, 2 }, // RGB in RGB order
151
{ 0, 1, 2, 3 } // RGBA in RGBA order
152
};
153
154     private int bitDepth;
155     private int colorType;
156
157     private int compressionMethod;
158     private int filterMethod;
159     private int interlaceMethod;
160     
161     private int paletteEntries;
162     private byte[] redPalette;
163     private byte[] greenPalette;
164     private byte[] bluePalette;
165     private byte[] alphaPalette;
166
167     private int bkgdRed;
168     private int bkgdGreen;
169     private int bkgdBlue;
170
171     private int grayTransparentAlpha;
172     private int redTransparentAlpha;
173     private int greenTransparentAlpha;
174     private int blueTransparentAlpha;
175
176     private int maxOpacity;
177
178     private int[] significantBits = null;
179
180     private boolean hasBackground = false;
181
182     // Parameter information
183

184     // If true, the user wants destination alpha where applicable.
185
private boolean suppressAlpha = false;
186
187     // If true, perform palette lookup internally
188
private boolean expandPalette = false;
189     
190     // If true, output < 8 bit gray images in 8 bit components format
191
private boolean output8BitGray = false;
192
193     // Create an alpha channel in the destination color model.
194
private boolean outputHasAlphaPalette = false;
195
196     // Perform gamma correction on the image
197
private boolean performGammaCorrection = false;
198
199     // Expand GA to GGGA for compatbility with Java2D
200
private boolean expandGrayAlpha = false;
201
202     // Produce an instance of PNGEncodeParam
203
private boolean generateEncodeParam = false;
204
205     // PNGDecodeParam controlling decode process
206
private PNGDecodeParam decodeParam = null;
207
208     // PNGEncodeParam to store file details in
209
private PNGEncodeParam encodeParam = null;
210
211     private boolean emitProperties = true;
212
213     private float fileGamma = 45455/100000.0F;
214
215     private float userExponent = 1.0F;
216
217     private float displayExponent = 2.2F;
218
219     private float[] chromaticity = null;
220
221     private int sRGBRenderingIntent = -1;
222
223     // Post-processing step implied by above parameters
224
private int postProcess = POST_NONE;
225
226     // Possible post-processing steps
227

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

450                     String JavaDoc type = chunk.getTypeString();
451                     byte[] data = chunk.getData();
452                     if (encodeParam != null) {
453                         encodeParam.addPrivateChunk(type, data);
454                     }
455                     if (emitProperties) {
456                         String JavaDoc key = "chunk_" + chunkIndex++ + ":" + type;
457                         properties.put(key.toLowerCase(), data);
458                     }
459                 }
460             } catch (Exception JavaDoc e) {
461                 e.printStackTrace();
462                 String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder2");
463                 throw new RuntimeException JavaDoc(msg);
464             }
465         } while (true);
466
467         // Final post-processing
468

469         if (significantBits == null) {
470             significantBits = new int[inputBands];
471             for (int i = 0; i < inputBands; i++) {
472                 significantBits[i] = bitDepth;
473             }
474
475             if (emitProperties) {
476                 properties.put("significant_bits", significantBits);
477             }
478         }
479     }
480
481     private static String JavaDoc getChunkType(DataInputStream JavaDoc distream) {
482         try {
483             distream.mark(8);
484             /* int length = */ distream.readInt();
485             int type = distream.readInt();
486             distream.reset();
487
488             String JavaDoc typeString = new String JavaDoc();
489             typeString += (char)(type >> 24);
490             typeString += (char)((type >> 16) & 0xff);
491             typeString += (char)((type >> 8) & 0xff);
492             typeString += (char)(type & 0xff);
493             return typeString;
494         } catch (Exception JavaDoc e) {
495             e.printStackTrace();
496             return null;
497         }
498     }
499
500     private static PNGChunk readChunk(DataInputStream JavaDoc distream) {
501         try {
502             int length = distream.readInt();
503             int type = distream.readInt();
504             byte[] data = new byte[length];
505             distream.readFully(data);
506             int crc = distream.readInt();
507             
508             return new PNGChunk(length, type, data, crc);
509         } catch (Exception JavaDoc e) {
510             e.printStackTrace();
511             return null;
512         }
513     }
514
515     private void parse_IHDR_chunk(PNGChunk chunk) {
516         int width = chunk.getInt4(0);
517         int height = chunk.getInt4(4);
518
519     bounds = new Rectangle JavaDoc(0, 0, width, height);
520
521         bitDepth = chunk.getInt1(8);
522         
523         if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4) &&
524             (bitDepth != 8) && (bitDepth != 16)) {
525             // Error -- bad bit depth
526
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder3");
527             throw new RuntimeException JavaDoc(msg);
528         }
529         maxOpacity = (1 << bitDepth) - 1;
530
531         colorType = chunk.getInt1(9);
532         if ((colorType != PNG_COLOR_GRAY) &&
533             (colorType != PNG_COLOR_RGB) &&
534             (colorType != PNG_COLOR_PALETTE) &&
535             (colorType != PNG_COLOR_GRAY_ALPHA) &&
536             (colorType != PNG_COLOR_RGB_ALPHA)) {
537             System.out.println(PropertyUtil.getString("PNGImageDecoder4"));
538         }
539
540         if ((colorType == PNG_COLOR_RGB) && (bitDepth < 8)) {
541             // Error -- RGB images must have 8 or 16 bits
542
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder5");
543             throw new RuntimeException JavaDoc(msg);
544         }
545
546         if ((colorType == PNG_COLOR_PALETTE) && (bitDepth == 16)) {
547             // Error -- palette images must have < 16 bits
548
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder6");
549             throw new RuntimeException JavaDoc(msg);
550         }
551
552         if ((colorType == PNG_COLOR_GRAY_ALPHA) && (bitDepth < 8)) {
553             // Error -- gray/alpha images must have >= 8 bits
554
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder7");
555             throw new RuntimeException JavaDoc(msg);
556         }
557
558         if ((colorType == PNG_COLOR_RGB_ALPHA) && (bitDepth < 8)) {
559             // Error -- RGB/alpha images must have >= 8 bits
560
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder8");
561             throw new RuntimeException JavaDoc(msg);
562         }
563
564         if (emitProperties) {
565             properties.put("color_type", colorTypeNames[colorType]);
566         }
567
568         if (generateEncodeParam) {
569             if (colorType == PNG_COLOR_PALETTE) {
570                 encodeParam = new PNGEncodeParam.Palette();
571             } else if (colorType == PNG_COLOR_GRAY ||
572                        colorType == PNG_COLOR_GRAY_ALPHA) {
573                 encodeParam = new PNGEncodeParam.Gray();
574             } else {
575                 encodeParam = new PNGEncodeParam.RGB();
576             }
577             decodeParam.setEncodeParam(encodeParam);
578         }
579
580         if (encodeParam != null) {
581             encodeParam.setBitDepth(bitDepth);
582         }
583         if (emitProperties) {
584             properties.put("bit_depth", new Integer JavaDoc(bitDepth));
585         }
586
587         if (performGammaCorrection) {
588             // Assume file gamma is 1/2.2 unless we get a gAMA chunk
589
float gamma = (1.0F/2.2F)*(displayExponent/userExponent);
590             if (encodeParam != null) {
591                 encodeParam.setGamma(gamma);
592             }
593             if (emitProperties) {
594                 properties.put("gamma", new Float JavaDoc(gamma));
595             }
596         }
597
598         compressionMethod = chunk.getInt1(10);
599         if (compressionMethod != 0) {
600             // Error -- only know about compression method 0
601
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder9");
602             throw new RuntimeException JavaDoc(msg);
603         }
604
605         filterMethod = chunk.getInt1(11);
606         if (filterMethod != 0) {
607             // Error -- only know about filter method 0
608
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder10");
609             throw new RuntimeException JavaDoc(msg);
610         }
611
612         interlaceMethod = chunk.getInt1(12);
613         if (interlaceMethod == 0) {
614             if (encodeParam != null) {
615                 encodeParam.setInterlacing(false);
616             }
617             if (emitProperties) {
618                 properties.put("interlace_method", "None");
619             }
620         } else if (interlaceMethod == 1) {
621             if (encodeParam != null) {
622                 encodeParam.setInterlacing(true);
623             }
624             if (emitProperties) {
625                 properties.put("interlace_method", "Adam7");
626             }
627         } else {
628             // Error -- only know about Adam7 interlacing
629
String JavaDoc msg = PropertyUtil.getString("PNGImageDecoder11");
630             throw new RuntimeException JavaDoc(msg);
631         }
632         
633         bytesPerPixel = (bitDepth == 16) ? 2 : 1;
634
635         switch (colorType) {
636         case PNG_COLOR_GRAY:
637             inputBands = 1;
638             outputBands = 1;
639
640             if (output8BitGray && (bitDepth < 8)) {
641                 postProcess = POST_GRAY_LUT;
642             } else if (performGammaCorrection) {
643                 postProcess = POST_GAMMA;
644             } else {
645                 postProcess = POST_NONE;
646             }
647             break;
648
649         case PNG_COLOR_RGB:
650             inputBands = 3;
651             bytesPerPixel *= 3;
652             outputBands = 3;
653
654             if (performGammaCorrection) {
655                 postProcess = POST_GAMMA;
656             } else {
657                 postProcess = POST_NONE;
658             }
659             break;
660
661         case PNG_COLOR_PALETTE:
662             inputBands = 1;
663             bytesPerPixel = 1;
664             outputBands = expandPalette ? 3 : 1;
665
666             if (expandPalette) {
667                 postProcess = POST_PALETTE_TO_RGB;
668             } else {
669                 postProcess = POST_NONE;
670             }
671             break;
672
673         case PNG_COLOR_GRAY_ALPHA:
674             inputBands = 2;
675             bytesPerPixel *= 2;
676
677             if (suppressAlpha) {
678                 outputBands = 1;
679                 postProcess = POST_REMOVE_GRAY_TRANS;
680             } else {
681                 if (performGammaCorrection) {
682                     postProcess = POST_GAMMA;
683                 } else {
684                     postProcess = POST_NONE;
685                 }
686                 if (expandGrayAlpha) {
687                     postProcess |= POST_EXP_MASK;
688                     outputBands = 4;
689                 } else {
690                     outputBands = 2;
691                 }
692             }
693             break;
694
695         case PNG_COLOR_RGB_ALPHA:
696             inputBands = 4;
697             bytesPerPixel *= 4;
698             outputBands = (!suppressAlpha) ? 4 : 3;
699
700             if (suppressAlpha) {
701                 postProcess = POST_REMOVE_RGB_TRANS;
702             } else if (performGammaCorrection) {
703                 postProcess = POST_GAMMA;
704             } else {
705                 postProcess = POST_NONE;
706             }
707             break;
708         }
709     }
710
711     private void parse_IEND_chunk(PNGChunk chunk) throws Exception JavaDoc {
712         // Store text strings
713
int textLen = textKeys.size();
714         String JavaDoc[] textArray = new String JavaDoc[2*textLen];
715         for (int i = 0; i < textLen; i++) {
716             String JavaDoc key = (String JavaDoc)textKeys.elementAt(i);
717             String JavaDoc val = (String JavaDoc)textStrings.elementAt(i);
718             textArray[2*i] = key;
719             textArray[2*i + 1] = val;
720             if (emitProperties) {
721                 String JavaDoc uniqueKey = "text_" + i + ":" + key;
722                 properties.put(uniqueKey.toLowerCase(), val);
723             }
724         }
725         if (encodeParam != null) {
726             encodeParam.setText(textArray);
727         }
728
729         // Store compressed text strings
730
int ztextLen = ztextKeys.size();
731         String JavaDoc[] ztextArray = new String JavaDoc[2*ztextLen];
732         for (int i = 0; i < ztextLen; i++) {
733