KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > codec > PngImage


1 /*
2  * Copyright 2003 by Paulo Soares.
3  *
4  * The contents of this file are subject to the Mozilla Public License Version 1.1
5  * (the "License"); you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the License.
11  *
12  * The Original Code is 'iText, a free JAVA-PDF library'.
13  *
14  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
15  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
16  * All Rights Reserved.
17  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
18  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
19  *
20  * Contributor(s): all the names of the contributors are added in the source code
21  * where applicable.
22  *
23  * Alternatively, the contents of this file may be used under the terms of the
24  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
25  * provisions of LGPL are applicable instead of those above. If you wish to
26  * allow use of your version of this file only under the terms of the LGPL
27  * License and not to allow others to use your version of this file under
28  * the MPL, indicate your decision by deleting the provisions above and
29  * replace them with the notice and other provisions required by the LGPL.
30  * If you do not delete the provisions above, a recipient may use your version
31  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
32  *
33  * This library is free software; you can redistribute it and/or modify it
34  * under the terms of the MPL as stated above or under the terms of the GNU
35  * Library General Public License as published by the Free Software Foundation;
36  * either version 2 of the License, or any later version.
37  *
38  * This library is distributed in the hope that it will be useful, but WITHOUT
39  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
41  * details.
42  *
43  * If you didn't download this code from the following link, you should check if
44  * you aren't using an obsolete version:
45  * http://www.lowagie.com/iText/
46  *
47  *
48  * The original JAI codecs have the following license
49  *
50  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
51  *
52  * Redistribution and use in source and binary forms, with or without
53  * modification, are permitted provided that the following conditions are met:
54  *
55  * -Redistributions of source code must retain the above copyright notice, this
56  * list of conditions and the following disclaimer.
57  *
58  * -Redistribution in binary form must reproduct the above copyright notice,
59  * this list of conditions and the following disclaimer in the documentation
60  * and/or other materials provided with the distribution.
61  *
62  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
63  * be used to endorse or promote products derived from this software without
64  * specific prior written permission.
65  *
66  * This software is provided "AS IS," without a warranty of any kind. ALL
67  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
68  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
69  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
70  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
71  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
72  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
73  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
74  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
75  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
76  * POSSIBILITY OF SUCH DAMAGES.
77  *
78  * You acknowledge that Software is not designed,licensed or intended for use in
79  * the design, construction, operation or maintenance of any nuclear facility.
80  */

81
82 package com.lowagie.text.pdf.codec;
83
84 import java.awt.color.ICC_Profile JavaDoc;
85 import java.io.ByteArrayInputStream JavaDoc;
86 import java.io.ByteArrayOutputStream JavaDoc;
87 import java.io.DataInputStream JavaDoc;
88 import java.io.IOException JavaDoc;
89 import java.io.InputStream JavaDoc;
90 import java.net.URL JavaDoc;
91 import java.util.zip.Inflater JavaDoc;
92 import java.util.zip.InflaterInputStream JavaDoc;
93
94 import com.lowagie.text.ExceptionConverter;
95 import com.lowagie.text.Image;
96 import com.lowagie.text.ImgRaw;
97 import com.lowagie.text.Utilities;
98 import com.lowagie.text.pdf.ByteBuffer;
99 import com.lowagie.text.pdf.PdfArray;
100 import com.lowagie.text.pdf.PdfDictionary;
101 import com.lowagie.text.pdf.PdfLiteral;
102 import com.lowagie.text.pdf.PdfName;
103 import com.lowagie.text.pdf.PdfNumber;
104 import com.lowagie.text.pdf.PdfObject;
105 import com.lowagie.text.pdf.PdfReader;
106 import com.lowagie.text.pdf.PdfString;
107
108 /** Reads a PNG image. All types of PNG can be read.
109  * <p>
110  * It is based in part in the JAI codec.
111  *
112  * @author Paulo Soares (psoares@consiste.pt)
113  */

114 public class PngImage {
115 /** Some PNG specific values. */
116     public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10};
117     
118 /** A PNG marker. */
119     public static final String JavaDoc IHDR = "IHDR";
120     
121 /** A PNG marker. */
122     public static final String JavaDoc PLTE = "PLTE";
123     
124 /** A PNG marker. */
125     public static final String JavaDoc IDAT = "IDAT";
126     
127 /** A PNG marker. */
128     public static final String JavaDoc IEND = "IEND";
129     
130 /** A PNG marker. */
131     public static final String JavaDoc tRNS = "tRNS";
132     
133 /** A PNG marker. */
134     public static final String JavaDoc pHYs = "pHYs";
135     
136 /** A PNG marker. */
137     public static final String JavaDoc gAMA = "gAMA";
138     
139 /** A PNG marker. */
140     public static final String JavaDoc cHRM = "cHRM";
141     
142 /** A PNG marker. */
143     public static final String JavaDoc sRGB = "sRGB";
144     
145 /** A PNG marker. */
146     public static final String JavaDoc iCCP = "iCCP";
147     
148     private static final int TRANSFERSIZE = 4096;
149     private static final int PNG_FILTER_NONE = 0;
150     private static final int PNG_FILTER_SUB = 1;
151     private static final int PNG_FILTER_UP = 2;
152     private static final int PNG_FILTER_AVERAGE = 3;
153     private static final int PNG_FILTER_PAETH = 4;
154     private static final PdfName intents[] = {PdfName.PERCEPTUAL,
155         PdfName.RELATIVECALORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECALORIMETRIC};
156     
157     InputStream JavaDoc is;
158     DataInputStream JavaDoc dataStream;
159     int width;
160     int height;
161     int bitDepth;
162     int colorType;
163     int compressionMethod;
164     int filterMethod;
165     int interlaceMethod;
166     PdfDictionary additional = new PdfDictionary();
167     byte image[];
168     byte smask[];
169     byte trans[];
170     NewByteArrayOutputStream idat = new NewByteArrayOutputStream();
171     int dpiX;
172     int dpiY;
173     float XYRatio;
174     boolean genBWMask;
175     boolean palShades;
176     int transRedGray = -1;
177     int transGreen = -1;
178     int transBlue = -1;
179     int inputBands;
180     int bytesPerPixel; // number of bytes per input pixel
181
byte colorTable[];
182     float gamma = 1f;
183     boolean hasCHRM = false;
184     float xW, yW, xR, yR, xG, yG, xB, yB;
185     PdfName intent;
186     ICC_Profile JavaDoc icc_profile;
187
188     
189     
190     /** Creates a new instance of PngImage */
191     PngImage(InputStream JavaDoc is) {
192         this.is = is;
193     }
194     
195     /** Reads a PNG from an url.
196      * @param url the url
197      * @throws IOException on error
198      * @return the image
199      */

200     public static Image getImage(URL JavaDoc url) throws IOException JavaDoc {
201         InputStream JavaDoc is = null;
202         try {
203             is = url.openStream();
204             Image img = getImage(is);
205             img.setUrl(url);
206             return img;
207         }
208         finally {
209             if (is != null) {
210                 is.close();
211             }
212         }
213     }
214     
215     /** Reads a PNG from a stream.
216      * @param is the stream
217      * @throws IOException on error
218      * @return the image
219      */

220     public static Image getImage(InputStream JavaDoc is) throws IOException JavaDoc {
221         PngImage png = new PngImage(is);
222         return png.getImage();
223     }
224     
225     /** Reads a PNG from a file.
226      * @param file the file
227      * @throws IOException on error
228      * @return the image
229      */

230     public static Image getImage(String JavaDoc file) throws IOException JavaDoc {
231         return getImage(Utilities.toURL(file));
232     }
233     
234     /** Reads a PNG from a byte array.
235      * @param data the byte array
236      * @throws IOException on error
237      * @return the image
238      */

239     public static Image getImage(byte data[]) throws IOException JavaDoc {
240         ByteArrayInputStream JavaDoc is = new ByteArrayInputStream JavaDoc(data);
241         Image img = getImage(is);
242         img.setOriginalData(data);
243         return img;
244     }
245     
246     boolean checkMarker(String JavaDoc s) {
247         if (s.length() != 4)
248             return false;
249         for (int k = 0; k < 4; ++k) {
250             char c = s.charAt(k);
251             if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
252                 return false;
253         }
254         return true;
255     }
256     
257     void readPng() throws IOException JavaDoc {
258         for (int i = 0; i < PNGID.length; i++) {
259             if (PNGID[i] != is.read()) {
260                 throw new IOException JavaDoc("File is not a valid PNG.");
261             }
262         }
263         byte buffer[] = new byte[TRANSFERSIZE];
264         while (true) {
265             int len = getInt(is);
266             String JavaDoc marker = getString(is);
267             if (len < 0 || !checkMarker(marker))
268                 throw new IOException JavaDoc("Corrupted PNG file.");
269             if (IDAT.equals(marker)) {
270                 int size;
271                 while (len != 0) {
272                     size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE));
273                     if (size < 0)
274                         return;
275                     idat.write(buffer, 0, size);
276                     len -= size;
277                 }
278             }
279             else if (tRNS.equals(marker)) {
280                 switch (colorType) {
281                     case 0:
282                         if (len >= 2) {
283                             len -= 2;
284                             int gray = getWord(is);
285                             if (bitDepth == 16)
286                                 transRedGray = gray;
287                             else
288                                 additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]"));
289                         }
290                         break;
291                     case 2:
292                         if (len >= 6) {
293                             len -= 6;
294                             int red = getWord(is);
295                             int green = getWord(is);
296                             int blue = getWord(is);
297                             if (bitDepth == 16) {
298                                 transRedGray = red;
299                                 transGreen = green;
300                                 transBlue = blue;
301                             }
302                             else
303                                 additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]"));
304                         }
305                         break;
306                     case 3:
307                         if (len > 0) {
308                             trans = new byte[len];
309                             for (int k = 0; k < len; ++k)
310                                 trans[k] = (byte)is.read();
311                             len = 0;
312                         }
313                         break;
314                 }
315                 Utilities.skip(is, len);
316             }
317             else if (IHDR.equals(marker)) {
318                 width = getInt(is);
319                 height = getInt(is);
320                 
321                 bitDepth = is.read();
322                 colorType = is.read();
323                 compressionMethod = is.read();
324                 filterMethod = is.read();
325                 interlaceMethod = is.read();
326             }
327             else if (PLTE.equals(marker)) {
328                 if (colorType == 3) {
329                     PdfArray colorspace = new PdfArray();
330                     colorspace.add(PdfName.INDEXED);
331                     colorspace.add(getColorspace());
332                     colorspace.add(new PdfNumber(len / 3 - 1));
333                     ByteBuffer colortable = new ByteBuffer();
334                     while ((len--) > 0) {
335                         colortable.append_i(is.read());
336                     }
337                     colorspace.add(new PdfString(colorTable = colortable.toByteArray()));
338                     additional.put(PdfName.COLORSPACE, colorspace);
339                 }
340                 else {
341                     Utilities.skip(is, len);
342                 }
343             }
344             else if (pHYs.equals(marker)) {
345                 int dx = getInt(is);
346                 int dy = getInt(is);
347                 int unit = is.read();
348                 if (unit == 1) {
349                     dpiX = (int)((float)dx * 0.0254f);
350                     dpiY = (int)((float)dy * 0.0254f);
351                 }
352                 else {
353                     if (dy != 0)
354                         XYRatio = (float)dx / (float)dy;
355                 }
356             }
357             else if (cHRM.equals(marker)) {
358                 xW = (float)getInt(is) / 100000f;
359                 yW = (float)getInt(is) / 100000f;
360                 xR = (float)getInt(is) / 100000f;
361                 yR = (float)getInt(is) / 100000f;
362                 xG = (float)getInt(is) / 100000f;
363                 yG = (float)getInt(is) / 100000f;
364                 xB = (float)getInt(is) / 100000f;
365                 yB = (float)getInt(is) / 100000f;
366                 hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f);
367             }
368             else if (sRGB.equals(marker)) {
369                 int ri = is.read();
370                 intent = intents[ri];
371                 gamma = 2.2f;
372                 xW = 0.3127f;
373                 yW = 0.329f;
374                 xR = 0.64f;
375                 yR = 0.33f;
376                 xG = 0.3f;
377                 yG = 0.6f;
378                 xB = 0.15f;
379                 yB = 0.06f;
380                 hasCHRM = true;
381             }
382             else if (gAMA.equals(marker)) {
383                 int gm = getInt(is);
384                 if (gm != 0) {
385                     gamma = 100000f / (float)gm;
386                     if (!hasCHRM) {
387                         xW = 0.3127f;
388                         yW = 0.329f;
389                         xR = 0.64f;
390                         yR = 0.33f;
391                         xG = 0.3f;
392                         yG = 0.6f;
393                         xB = 0.15f;
394                         yB = 0.06f;
395                         hasCHRM = true;
396                     }
397                 }
398             }
399             else if (iCCP.equals(marker)) {
400                 do {
401                     --len;
402                 } while (is.read() != 0);
403                 is.read();
404                 --len;
405                 byte icccom[] = new byte[len];
406                 int p = 0;
407                 while (len > 0) {
408                     int r = is.read(icccom, p, len);
409                     if (r < 0)
410                         throw new IOException JavaDoc("Premature end of file.");
411                     p += r;
412                     len -= r;
413                 }
414                 byte iccp[] = PdfReader.FlateDecode(icccom, true);
415                 icccom = null;
416                 try {
417                     icc_profile = ICC_Profile.getInstance(iccp);
418                 }
419                 catch (RuntimeException JavaDoc e) {
420                     icc_profile = null;
421                 }
422             }
423             else if (IEND.equals(marker)) {
424                 break;
425             }
426             else {
427                 Utilities.skip(is, len);
428             }
429             Utilities.skip(is, 4);
430         }
431     }
432     
433     PdfObject getColorspace() {
434         if (icc_profile != null) {
435             if ((colorType & 2) == 0)
436                 return PdfName.DEVICEGRAY;
437             else
438                 return PdfName.DEVICERGB;
439         }
440         if (gamma == 1f && !hasCHRM) {
441             if ((colorType & 2) == 0)
442                 return PdfName.DEVICEGRAY;
443             else
444                 return PdfName.DEVICERGB;
445         }
446         else {
447             PdfArray array = new PdfArray();
448             PdfDictionary dic = new PdfDictionary();
449             if ((colorType & 2) == 0) {
450                 if (gamma == 1f)
451                     return PdfName.DEVICEGRAY;
452                 array.add(PdfName.CALGRAY);
453                 dic.put(PdfName.GAMMA, new PdfNumber(gamma));
454                 dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]"));
455                 array.add(dic);
456             }
457             else {
458                 PdfObject wp = new PdfLiteral("[1 1 1]");
459                 array.add(PdfName.CALRGB);
460                 if (gamma != 1f) {
461                     PdfArray gm = new PdfArray();
462                     PdfNumber n = new PdfNumber(gamma);
463                     gm.add(n);
464                     gm.add(n);
465                     gm.add(n);
466                     dic.put(PdfName.GAMMA, gm);
467                 }
468                 if (hasCHRM) {
469                     float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB);
470                     float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z;
471                     float XA = YA*xR/yR;
472                     float ZA = YA*((1-xR)/yR-1);
473                     float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z;
474                     float XB = YB*xG/yG;
475                     float ZB = YB*((1-xG)/yG-1);
476                     float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z;
477                     float XC = YC*xB/yB;
478                     float ZC = YC*((1-xB)/yB-1);
479                     float XW = XA+XB+XC;
480                     float YW = 1;//YA+YB+YC;
481
float ZW = ZA+ZB+ZC;
482                     PdfArray wpa = new PdfArray();
483                     wpa.add(new PdfNumber(XW));
484                     wpa.add(new PdfNumber(YW));
485                     wpa.add(new PdfNumber(ZW));
486                     wp = wpa;
487                     PdfArray matrix = new PdfArray();
488                     matrix.add(new PdfNumber(XA));
489                     matrix.add(new PdfNumber(YA));
490                     matrix.add(new PdfNumber(ZA));
491                     matrix.add(new PdfNumber(XB));
492                     matrix.add(new PdfNumber(YB));
493                     matrix.add(new PdfNumber(ZB));
494                     matrix.add(new PdfNumber(XC));
495                     matrix.add(new PdfNumber(YC));
496                     matrix.add(new PdfNumber(ZC));
497                     dic.put(PdfName.MATRIX, matrix);
498                 }
499                 dic.put(PdfName.WHITEPOINT, wp);
500                 array.add(dic);
501             }
502             return array;
503         }
504     }
505     
506     Image getImage() throws IOException JavaDoc {
507         readPng();
508         try {
509             int pal0 = 0;
510             int palIdx = 0;
511             palShades = false;
512             if (trans != null) {
513                 for (int k = 0; k < trans.length; ++k) {
514                     int n = trans[k] & 0xff;
515                     if (n == 0) {
516                         ++pal0;
517                         palIdx = k;
518                     }
519                     if (n != 0 && n != 255) {
520                         palShades = true;
521                         break;
522                     }
523                 }
524             }
525             if ((colorType & 4) != 0)
526                 palShades = true;
527             genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0));
528             if (!palShades && !genBWMask && pal0 == 1) {
529                 additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]"));
530             }
531             boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask;
532             switch (colorType) {
533                 case 0:
534                     inputBands = 1;
535                     break;
536                 case 2:
537                     inputBands = 3;
538                     break;
539                 case 3:
540                     inputBands = 1;
541                     break;
542                 case 4:
543                     inputBands = 2;
544                     break;
545                 case 6:
546                     inputBands = 4;
547                     break;
548             }
549             if (needDecode)
550                 decodeIdat();
551             int components = inputBands;
552             if ((colorType & 4) != 0)
553                 --components;
554             int bpc = bitDepth;
555             if (bpc == 16)
556                 bpc = 8;
557             Image img;
558             if (image != null)
559                 img = Image.getInstance(width, height, components, bpc, image);
560             else {
561                 img = new ImgRaw(width, height, components, bpc, idat.toByteArray());
562                 img.setDeflated(true);
563                 PdfDictionary decodeparms = new PdfDictionary();
564                 decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth));
565                 decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15));
566                 decodeparms.put(PdfName.COLUMNS, new PdfNumber(width));
567                 decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3));
568                 additional.put(PdfName.DECODEPARMS, decodeparms);
569             }
570             if (additional.get(PdfName.COLORSPACE) == null)
571                 additional.put(PdfName.COLORSPACE, getColorspace());
572             if (intent != null)
573                 additional.put(PdfName.INTENT, intent);
574             if (additional.size() > 0)
575                 img.setAdditional(additional);
576             if (icc_profile != null)
577                 img.tagICC(icc_profile);
578             if (palShades) {
579                 Image im2 = Image.getInstance(width, height, 1, 8, smask);
580                 im2.makeMask();
581                 img.setImageMask(im2);
582             }
583             if (genBWMask) {
584                 Image im2 = Image.getInstance(width, height, 1, 1, smask);
585                 im2.makeMask();
586                 img.setImageMask(im2);
587             }
588             img.setDpi(dpiX, dpiY);
589             img.setXYRatio(XYRatio);
590             img.setOriginalType(Image.ORIGINAL_PNG);
591             return img;
592         }
593         catch (Exception JavaDoc e) {
594             throw new ExceptionConverter(e);
595         }
596     }
597     
598     void decodeIdat() {
599         int nbitDepth = bitDepth;
600         if (nbitDepth == 16)
601             nbitDepth = 8;
602         int size = -1;
603         bytesPerPixel = (bitDepth == 16) ? 2 : 1;
604         switch (colorType) {
605             case 0:
606                 size = (nbitDepth * width + 7) / 8 * height;
607                 break;
608             case 2:
609                 size = width * 3 * height;
610                 bytesPerPixel *= 3;
611                 break;
612             case 3:
613                 if (interlaceMethod == 1)
614                     size = (nbitDepth * width + 7) / 8 * height;
615                 bytesPerPixel = 1;
616                 break;
617             case 4:
618                 size = width * height;
619                 bytesPerPixel *= 2;
620                 break;
621             case 6:
622                 size = width * 3 * height;
623                 bytesPerPixel *= 4;
624                 break;
625         }
626         if (size >= 0)
627             image = new byte[size];
628         if (palShades)
629             smask = new byte[width * height];
630         else if (genBWMask)
631             smask = new byte[(width + 7) / 8 * height];
632         ByteArrayInputStream JavaDoc bai = new ByteArrayInputStream JavaDoc(idat.getBuf(), 0, idat.size());
633         InputStream JavaDoc infStream = new InflaterInputStream JavaDoc(bai, new Inflater JavaDoc());
634         dataStream = new DataInputStream JavaDoc(infStream);
635         
636         if (interlaceMethod != 1) {
637             decodePass(0, 0, 1, 1, width, height);
638         }
639         else {
640             decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8);
641             decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8);
642             decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8);
643             decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4);
644             decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4);
645             decodePass(1, 0, 2, 2, width/2, (height + 1)/2);
646             decodePass(0, 1, 1, 2, width, height/2);
647         }
648         
649     }
650     
651     void decodePass( int xOffset, int yOffset,
652     int xStep, int yStep,
653     int passWidth, int passHeight) {
654         if ((passWidth == 0) || (passHeight == 0)) {
655             return;
656         }
657         
658         int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
659         byte[] curr = new byte[bytesPerRow];
660         byte[] prior = new byte[bytesPerRow];
661         
662         // Decode the (sub)image row-by-row
663
int srcY, dstY;
664         for (srcY = 0, dstY = yOffset;
665         srcY < passHeight;
666         srcY++, dstY += yStep) {
667             // Read the filter type byte and a row of data
668
int filter = 0;
669             try {
670                 filter = dataStream.read();
671                 dataStream.readFully(curr, 0, bytesPerRow);
672             } catch (Exception JavaDoc e) {
673                 // empty on purpose
674
}
675             
676             switch (filter) {
677                 case PNG_FILTER_NONE:
678                     break;
679                 case PNG_FILTER_SUB:
680                     decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
681                     break;
682                 case PNG_FILTER_UP:
683                     decodeUpFilter(curr, prior, bytesPerRow);
684                     break;
685                 case PNG_FILTER_AVERAGE:
686                     decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
687                     break;
688                 case PNG_FILTER_PAETH:
689                     decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
690                     break;
691                 default:
692                     // Error -- uknown filter type
693
throw new RuntimeException JavaDoc("PNG filter unknown.");
694             }
695             
696             processPixels(curr, xOffset, xStep, dstY, passWidth);
697             
698             // Swap curr and prior
699
byte[] tmp = prior;
700             prior = curr;
701             curr = tmp;
702         }
703     }
704     
705     void processPixels(byte curr[], int xOffset, int step, int y, int width) {
706         int srcX, dstX;
707
708         int out[] = getPixel(curr);
709         int sizes = 0;
710         switch (colorType) {
711             case 0:
712             case 3:
713             case 4:
714                 sizes = 1;
715                 break;
716             case 2:
717             case 6:
718                 sizes = 3;
719                 break;
720         }
721         if (image != null) {
722             dstX = xOffset;
723             int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8;
724             for (srcX = 0; srcX < width; srcX++) {
725                 setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride);
726                 dstX += step;
727             }
728         }
729         if (palShades) {
730             if ((colorType & 4) != 0) {
731                 if (bitDepth == 16) {
732                     for (int k = 0; k < width; ++k)
733                         out[k * inputBands + sizes] >>>= 8;
734                 }
735                 int yStride = this.width;
736                 dstX = xOffset;
737                 for (srcX = 0; srcX < width; srcX++) {
738                     setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride);
739                     dstX += step;
740                 }
741             }
742             else { //colorType 3
743
int yStride = this.width;
744                 int v[] = new int[1];
745                 dstX = xOffset;
746                 for (srcX = 0; srcX < width; srcX++) {
747                     int idx = out[srcX];
748                     if (idx < trans.length)
749                         v[0] = trans[idx];
750                     setPixel(smask, v, 0, 1, dstX, y, 8, yStride);
751                     dstX += step;
752                 }
753             }
754         }
755         else if (genBWMask) {
756             switch (colorType) {
757                 case 3: {
758                     int yStride = (this.width + 7) / 8;
759                     int v[] = new int[1];
760                     dstX = xOffset;
761                     for (srcX = 0; srcX < width; srcX++) {
762                         int idx = out[srcX];
763                         if (idx < trans.length)
764                             v[0] = (trans[idx] == 0 ? 1 : 0);
765                         setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
766                         dstX += step;
767                     }
768                     break;
769                 }
770                 case 0: {
771                     int yStride = (this.width + 7) / 8;
772                     int v[] = new int[1];
773                     dstX = xOffset;
774                     for (srcX = 0; srcX < width; srcX++) {
775                         int g = out[srcX];
776                         v[0] = (g == transRedGray ? 1 : 0);
777                         setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
778                         dstX += step;
779                     }
780                     break;
781                 }
782                 case 2: {
783                     int yStride = (this.width + 7) / 8;
784                     int v[] = new int[1];
785                     dstX = xOffset;
786                     for (srcX = 0; srcX < width; srcX++) {
787                         int markRed = inputBands * srcX;
788                         v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen
789                             && out[markRed + 2] == transBlue ? 1 : 0);
790                         setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
791                         dstX += step;
792                     }
793                     break;
794                 }
795             }
796         }
797     }
798     
799     static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) {
800         if (bitDepth == 8) {
801             int pos = bytesPerRow * y + x;
802             return image[pos] & 0xff;
803         }
804         else {
805             int pos = bytesPerRow * y + x / (8 / bitDepth);
806             int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
807             return v & ((1 << bitDepth) - 1);
808         }
809     }
810     
811     static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) {
812         if (bitDepth == 8) {
813             int pos = bytesPerRow * y + size * x;
814             for (int k = 0; k < size; ++k)
815                 image[pos + k] = (byte)data[k + offset];
816         }
817         else if (bitDepth == 16) {
818             int pos = bytesPerRow * y + size * x;
819             for (int k = 0; k < size; ++k)
820                 image[pos + k] = (byte)(data[k + offset] >>> 8);
821         }
822         else {
823             int pos = bytesPerRow * y + x / (8 / bitDepth);
824             int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
825             image[pos] |= v;
826         }
827     }
828     
829     int[] getPixel(byte curr[]) {
830         switch (bitDepth) {
831             case 8: {
832                 int out[] = new int[curr.length];
833                 for (int k = 0; k < out.length; ++k)
834                     out[k] = curr[k] & 0xff;
835                 return out;
836             }
837             case 16: {
838                 int out[] = new int[curr.length / 2];
839                 for (int k = 0; k < out.length; ++k)
840                     out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff);
841                 return out;
842             }
843             default: {
844                 int out[] = new int[curr.length * 8 / bitDepth];
845                 int idx = 0;
846                 int passes = 8 / bitDepth;
847                 int mask = (1 << bitDepth) - 1;
848                 for (int k = 0; k < curr.length; ++k) {
849                     for (int j = passes - 1; j >= 0; --j) {
850                         out[idx++] = (curr[k] >>> (bitDepth * j)) & mask;
851                     }
852                 }
853                 return out;
854             }
855         }
856     }
857     
858     private static void decodeSubFilter(byte[] curr, int count, int bpp) {
859         for (int i = bpp; i < count; i++) {
860             int val;
861             
862             val = curr[i] & 0xff;
863             val += curr[i - bpp] & 0xff;
864             
865             curr[i] = (byte)val;
866         }
867     }
868     
869     private static void decodeUpFilter(byte[] curr, byte[] prev,
870     int count) {
871         for (int i = 0; i < count; i++) {
872             int raw = curr[i] & 0xff;
873             int prior = prev[i] & 0xff;
874             
875             curr[i] = (byte)(raw + prior);
876         }
877     }
878     
879     private static void decodeAverageFilter(byte[] curr, byte[] prev,
880     int count, int bpp) {
881         int raw, priorPixel, priorRow;
882         
883         for (int i = 0; i < bpp; i++) {
884             raw = curr[i] & 0xff;
885             priorRow = prev[i] & 0xff;
886             
887             curr[i] = (byte)(raw + priorRow/2);
888         }
889         
890         for (int i = bpp; i < count; i++) {
891             raw = curr[i] & 0xff;
892             priorPixel = curr[i - bpp] & 0xff;
893             priorRow = prev[i] & 0xff;
894             
895             curr[i] = (byte)(raw + (priorPixel + priorRow)/2);
896         }
897     }
898     
899     private static int paethPredictor(int a, int b, int c) {
900         int p = a + b - c;
901         int pa = Math.abs(p - a);
902         int pb = Math.abs(p - b);
903         int pc = Math.abs(p - c);
904         
905         if ((pa <= pb) && (pa <= pc)) {
906             return a;
907         } else if (pb <= pc) {
908             return b;
909         } else {
910             return c;
911         }
912     }
913     
914     private static void decodePaethFilter(byte[] curr, byte[] prev,
915     int count, int bpp) {
916         int raw, priorPixel, priorRow, priorRowPixel;
917         
918         for (int i = 0; i < bpp; i++) {
919             raw = curr[i] & 0xff;
920             priorRow = prev[i] & 0xff;
921             
922             curr[i] = (byte)(raw + priorRow);
923         }
924         
925         for (int i = bpp; i < count; i++) {
926             raw = curr[i] & 0xff;
927             priorPixel = curr[i - bpp] & 0xff;
928             priorRow = prev[i] & 0xff;
929             priorRowPixel = prev[i - bpp] & 0xff;
930             
931             curr[i] = (byte)(raw + paethPredictor(priorPixel,
932             priorRow,
933             priorRowPixel));
934         }
935     }
936     
937     static class NewByteArrayOutputStream extends ByteArrayOutputStream JavaDoc {
938         public byte[] getBuf() {
939             return buf;
940         }
941     }
942
943 /**
944  * Gets an <CODE>int</CODE> from an <CODE>InputStream</CODE>.
945  *
946  * @param is an <CODE>InputStream</CODE>
947  * @return the value of an <CODE>int</CODE>
948  */

949     
950     public static final int getInt(InputStream JavaDoc is) throws IOException JavaDoc {
951         return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read();
952     }
953     
954 /**
955  * Gets a <CODE>word</CODE> from an <CODE>InputStream</CODE>.
956  *
957  * @param is an <CODE>InputStream</CODE>
958  * @return the value of an <CODE>int</CODE>
959  */

960     
961     public static final int getWord(InputStream JavaDoc is) throws IOException JavaDoc {
962         return (is.read() << 8) + is.read();
963     }
964     
965 /**
966  * Gets a <CODE>String</CODE> from an <CODE>InputStream</CODE>.
967  *
968  * @param is an <CODE>InputStream</CODE>
969  * @return the value of an <CODE>int</CODE>
970  */

971     
972     public static final String JavaDoc getString(InputStream JavaDoc is) throws IOException JavaDoc {
973         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
974         for (int i = 0; i < 4; i++) {
975             buf.append((char)is.read());
976         }
977         return buf.toString();
978     }
979
980 }
981
Popular Tags