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             String JavaDoc key = (String JavaDoc)ztextKeys.elementAt(i);
734             String JavaDoc val = (String JavaDoc)ztextStrings.elementAt(i);
735             ztextArray[2*i] = key;
736             ztextArray[2*i + 1] = val;
737             if (emitProperties) {
738                 String JavaDoc uniqueKey = "ztext_" + i + ":" + key;
739                 properties.put(uniqueKey.toLowerCase(), val);
740             }
741         }
742         if (encodeParam != null) {
743             encodeParam.setCompressedText(ztextArray);
744         }
745
746         // Parse prior IDAT chunks
747
InputStream JavaDoc seqStream =
748             new SequenceInputStream JavaDoc(streamVec.elements());
749         InputStream JavaDoc infStream =
750             new InflaterInputStream JavaDoc(seqStream, new Inflater JavaDoc());
751         dataStream = new DataInputStream JavaDoc(infStream);
752         
753         // Create an empty WritableRaster
754
int depth = bitDepth;
755         if ((colorType == PNG_COLOR_GRAY) &&
756             (bitDepth < 8) && output8BitGray) {
757             depth = 8;
758         }
759         if ((colorType == PNG_COLOR_PALETTE) && expandPalette) {
760             depth = 8;
761         }
762     int width = bounds.width;
763     int height = bounds.height;
764
765         int bytesPerRow = (outputBands*width*depth + 7)/8;
766         int scanlineStride =
767             (depth == 16) ? (bytesPerRow/2) : bytesPerRow;
768
769         theTile = createRaster(width, height, outputBands,
770                                scanlineStride,
771                                depth);
772
773         if (performGammaCorrection && (gammaLut == null)) {
774             initGammaLut(bitDepth);
775         }
776         if ((postProcess == POST_GRAY_LUT) ||
777             (postProcess == POST_GRAY_LUT_ADD_TRANS) ||
778             (postProcess == POST_GRAY_LUT_ADD_TRANS_EXP)) {
779             initGrayLut(bitDepth);
780         }
781
782         decodeImage(interlaceMethod == 1);
783         SampleModel JavaDoc sm = theTile.getSampleModel();
784     ColorModel JavaDoc cm;
785
786         if ((colorType == PNG_COLOR_PALETTE) && !expandPalette) {
787             if (outputHasAlphaPalette) {
788                 cm = new IndexColorModel JavaDoc(bitDepth,
789                                                  paletteEntries,
790                                                  redPalette,
791                                                  greenPalette,
792                                                  bluePalette,
793                                                  alphaPalette);
794             } else {
795                 cm = new IndexColorModel JavaDoc(bitDepth,
796                                                  paletteEntries,
797                                                  redPalette,
798                                                  greenPalette,
799                                                  bluePalette);
800             }
801         } else if ((colorType == PNG_COLOR_GRAY) &&
802                    (bitDepth < 8) && !output8BitGray) {
803             byte[] palette = expandBits[bitDepth];
804             cm = new IndexColorModel JavaDoc(bitDepth,
805                                              palette.length,
806                                              palette,
807                                              palette,
808                                              palette);
809         } else {
810             cm =
811                 createComponentColorModel(sm);
812         }
813
814     init((CachableRed)null, bounds, cm, sm, 0, 0, properties);
815     }
816
817     private static final int[] GrayBits8 = { 8 };
818     private static final ComponentColorModel JavaDoc colorModelGray8 =
819         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
820                                 GrayBits8, false, false,
821                                 Transparency.OPAQUE,
822                                 DataBuffer.TYPE_BYTE);
823
824     private static final int[] GrayAlphaBits8 = { 8, 8 };
825     private static final ComponentColorModel JavaDoc colorModelGrayAlpha8 =
826         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
827                                 GrayAlphaBits8, true, false,
828                                 Transparency.TRANSLUCENT,
829                                 DataBuffer.TYPE_BYTE);
830
831     private static final int[] GrayBits16 = { 16 };
832     private static final ComponentColorModel JavaDoc colorModelGray16 =
833         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
834                                 GrayBits16, false, false,
835                                 Transparency.OPAQUE,
836                                 DataBuffer.TYPE_USHORT);
837
838     private static final int[] GrayAlphaBits16 = { 16, 16 };
839     private static final ComponentColorModel JavaDoc colorModelGrayAlpha16 =
840         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
841                                 GrayAlphaBits16, true, false,
842                                 Transparency.TRANSLUCENT,
843                                 DataBuffer.TYPE_USHORT);
844
845     private static final int[] GrayBits32 = { 32 };
846     private static final ComponentColorModel JavaDoc colorModelGray32 =
847         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
848                                 GrayBits32, false, false,
849                                 Transparency.OPAQUE,
850                                 DataBuffer.TYPE_INT);
851
852     private static final int[] GrayAlphaBits32 = { 32, 32 };
853     private static final ComponentColorModel JavaDoc colorModelGrayAlpha32 =
854         new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_GRAY),
855                                 GrayAlphaBits32, true, false,
856                                 Transparency.TRANSLUCENT,
857                                 DataBuffer.TYPE_INT);
858
859     private static final int[] RGBBits8 = { 8, 8, 8 };
860     private static final ComponentColorModel JavaDoc colorModelRGB8 =
861       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
862                               RGBBits8, false, false,
863                               Transparency.OPAQUE,
864                               DataBuffer.TYPE_BYTE);
865
866     private static final int[] RGBABits8 = { 8, 8, 8, 8 };
867     private static final ComponentColorModel JavaDoc colorModelRGBA8 =
868       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
869                               RGBABits8, true, false,
870                               Transparency.TRANSLUCENT,
871                               DataBuffer.TYPE_BYTE);
872
873     private static final int[] RGBBits16 = { 16, 16, 16 };
874     private static final ComponentColorModel JavaDoc colorModelRGB16 =
875       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
876                               RGBBits16, false, false,
877                               Transparency.OPAQUE,
878                               DataBuffer.TYPE_USHORT);
879
880     private static final int[] RGBABits16 = { 16, 16, 16, 16 };
881     private static final ComponentColorModel JavaDoc colorModelRGBA16 =
882       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
883                               RGBABits16, true, false,
884                               Transparency.TRANSLUCENT,
885                               DataBuffer.TYPE_USHORT);
886
887     private static final int[] RGBBits32 = { 32, 32, 32 };
888     private static final ComponentColorModel JavaDoc colorModelRGB32 =
889       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
890                               RGBBits32, false, false,
891                               Transparency.OPAQUE,
892                               DataBuffer.TYPE_INT);
893
894     private static final int[] RGBABits32 = { 32, 32, 32, 32 };
895     private static final ComponentColorModel JavaDoc colorModelRGBA32 =
896       new ComponentColorModel JavaDoc(ColorSpace.getInstance(ColorSpace.CS_sRGB),
897                               RGBABits32, true, false,
898                               Transparency.TRANSLUCENT,
899                               DataBuffer.TYPE_INT);
900     /**
901      * A convenience method to create an instance of
902      * <code>ComponentColorModel</code> suitable for use with the
903      * given <code>SampleModel</code>. The <code>SampleModel</code>
904      * should have a data type of <code>DataBuffer.TYPE_BYTE</code>,
905      * <code>TYPE_USHORT</code>, or <code>TYPE_INT</code> and between
906      * 1 and 4 bands. Depending on the number of bands of the
907      * <code>SampleModel</code>, either a gray, gray+alpha, rgb, or
908      * rgb+alpha <code>ColorModel</code> is returned.
909      */

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

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

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