KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > iv > flash > util > PNGHelper


1 /*
2  * $Id: PNGHelper.java,v 1.3 2002/06/27 21:49:17 awason Exp $
3  *
4  * ===========================================================================
5  *
6  * The JGenerator Software License, Version 1.0
7  *
8  * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowlegement:
23  * "This product includes software developed by Dmitry Skavish
24  * (skavish@usa.net, http://www.flashgap.com/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The name "The JGenerator" must not be used to endorse or promote
29  * products derived from this software without prior written permission.
30  * For written permission, please contact skavish@usa.net.
31  *
32  * 5. Products derived from this software may not be called "The JGenerator"
33  * nor may "The JGenerator" appear in their names without prior written
34  * permission of Dmitry Skavish.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
40  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  *
49  */

50
51 package org.openlaszlo.iv.flash.util;
52
53 import java.io.EOFException JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.io.InputStream JavaDoc;
56 import java.io.DataInputStream JavaDoc;
57 import java.io.BufferedInputStream JavaDoc;
58 import java.io.ByteArrayInputStream JavaDoc;
59 import java.util.zip.InflaterInputStream JavaDoc;
60 import java.util.zip.Deflater JavaDoc;
61 import java.util.zip.CRC32 JavaDoc;
62 import java.util.Hashtable JavaDoc;
63 import org.openlaszlo.iv.flash.parser.DataMarker;
64
65 /**
66  * Helper class to decode PNG images.
67  * Does read but not support gAMA chunk (yet :-)
68  * @author Patrick Talbot
69  */

70 public class PNGHelper
71 {
72     /** header chunk */
73     private static final int CHUNK_IHDR = 0x49484452;
74     /** palette chunk */
75     private static final int CHUNK_PLTE = 0x504c5445;
76     /** data chunk */
77     private static final int CHUNK_IDAT = 0x49444154;
78     /** end of file chunk */
79     private static final int CHUNK_IEND = 0x49454e44;
80     /** transparency chunk */
81     private static final int CHUNK_tRNS = 0x74524e53;
82
83     /** background (ignored) chunk */
84     private static final int CHUNK_bKGD = 0x624b4744;
85     /** chromaticities (ignored) chunk */
86     private static final int CHUNK_cHRM = 0x6348524d;
87     /** fractal image parameters (ignored) chunk */
88     private static final int CHUNK_fRAc = 0x66524163;
89     /** gamma (ignored) chunk */
90     private static final int CHUNK_gAMA = 0x67414d41;
91     /** GIF graphic control (ignored) chunk */
92     private static final int CHUNK_gIFg = 0x67494667;
93     /** GIF plain text (ignored) chunk */
94     private static final int CHUNK_gIFt = 0x67494674;
95     /** GIF application (ignored) chunk */
96     private static final int CHUNK_gIFx = 0x67494678;
97     /** histogram (ignored) chunk */
98     private static final int CHUNK_hIST = 0x68495354;
99     /** embedded ICC profile (ignored) chunk */
100     private static final int CHUNK_iCCP = 0x69434350;
101     /** unicode UTF-8 text (ignored) chunk */
102     private static final int CHUNK_iTXt = 0x69545874;
103     /** image offset (ignored) chunk */
104     private static final int CHUNK_oFFs = 0x6f464673;
105     /** calibration (ignored) chunk */
106     private static final int CHUNK_pCAL = 0x7043414c;
107     /** physical pixel dimension (ignored) chunk */
108     private static final int CHUNK_pHYs = 0x70485973;
109     /** significant bits (ignored) chunk */
110     private static final int CHUNK_sBIT = 0x73424954;
111     /** physical scale (ignored) chunk */
112     private static final int CHUNK_sCAL = 0x7343414c;
113     /** suggested palette (ignored) chunk */
114     private static final int CHUNK_sPLT = 0x73504c54;
115     /** standard RGB colour space (ignored) chunk */
116     private static final int CHUNK_sRGB = 0x73524742;
117     /** normal text (ignored) chunk */
118     private static final int CHUNK_tEXt = 0x74455874;
119     /** time stamp (ignored) chunk */
120     private static final int CHUNK_tIME = 0x74494d45;
121     /** compressed text (ignored) chunk */
122     private static final int CHUNK_zTXt = 0x7a545874;
123
124     /** PNG Signature */
125     private static final int[] PNG_SIGN = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
126
127     /** a flag to test if the header chunk has been encountered */
128     private boolean flagIHDR = false;
129     /** a flag to test if data chunks have been encountered */
130     private boolean flagIDAT = false;
131     /** a flag to test if the palette chunk has been encountered */
132     private boolean flagPLTE = false;
133
134     /** flag used to indicate that further computing is need to manage transparency */
135     private boolean tRNSPassRequired = false;
136     /** transparency color for colorType 0 and 2 */
137     private RGB tRNSKeyColor = null;
138     /** used to determine if transparency chunk exists */
139     private boolean flagtRNS = false;
140
141     /** number of bytes used per pixel */
142     private int colorType_components= -1;
143
144     /** compression method should always be 0 = deflate/inflate, */
145     private int compressionMethod = -1;
146     /** filter method can be
147     * 0 = none
148     * 1 = sub
149     * 2 = up
150     * 3 = average
151     * 4 = paeth
152     */

153     private int filterMethod = -1;
154     /** interlace method can be
155     * 0 = not interlace
156     * 1 = Adam7 interlacing
157     */

158     private int interlaceMethod = -1;
159
160     /** width of image*/
161     private int width = -1;
162     /** height of image */
163     private int height = -1;
164     /** pixels bit depth can be 1,2,4,8,16 */
165     private int bitDepth = -1;
166
167     /** gamma value of image, set to default value */
168     private long gamma = 45455;
169
170     /** color type can be
171     * 0 = gray
172     * 2 = RGB
173     * 3 = paletted
174     * 4 = gray + alpha
175     * 6 = RGB + alpha
176     */

177     private int colorType = -1;
178
179     /** flag set if palette is used */
180     private boolean colorType_hasPalette= false;
181     /** flag set if alpha channel exists for colorType = 4 or 6 */
182     private boolean colorType_hasAlpha = false;
183     /** flag set if not gray */
184     private boolean colorType_hasColor = false;
185
186     /** RGB Palette */
187     private RGB[] palette;
188
189     /** size of the palette */
190     private int colorsUsed = 0;
191
192     /** main IDAT buffer */
193     private byte[] buffer;
194
195     /** main filters per row buffer */
196     //private int[] filters;
197

198     /** Stream to read data from */
199     private CRCInputStream source;
200
201         /**
202          * A private input stream class used to read multi-byte integer values
203          * while maintaining CRC code for read bytes.
204          * Provides utility methods to read multi-byte integer values
205          * in both little- and bit-endian orders.
206          */

207         //private
208
public class CRCInputStream extends DataInputStream JavaDoc
209         {
210             /** crc checker */
211             private CRC32 JavaDoc crc = new CRC32 JavaDoc();
212
213             /**
214             * Constructor
215             * @param inputStream java.io.InputStream
216             */

217             public CRCInputStream(InputStream JavaDoc inputStream)
218             {
219                 super(inputStream);
220             }
221
222             /**
223             * Return the crc value
224             * @return long
225             */

226             public long getCRC()
227             {
228                 return crc.getValue();
229             }
230
231             /**
232             * Reset the crc checker
233             */

234             public void reset()
235             {
236                 crc.reset();
237             }
238
239             /**
240             * Read one int and ensure the crc is updated
241             * @return int
242             */

243             public int read() throws IOException JavaDoc
244             {
245                 int x = super.read();
246                 crc.update(x);
247                 return x;
248             }
249
250             /**
251             * Read two ints
252             * @return int
253             */

254             public int read2() throws IOException JavaDoc, EOFException JavaDoc
255             {
256                 int a = read();
257                 int b = read();
258                 return a | b << 8;
259             }
260
261             /**
262             * Read two ints in reverse order
263             * @return int
264             */

265             public int read2n() throws IOException JavaDoc, EOFException JavaDoc
266             {
267                 int b = read();
268                 int a = read();
269                 return a | b << 8;
270             }
271
272             /**
273             * Read a long in reverse order
274             * @return long
275             */

276             public long read4n() throws IOException JavaDoc, EOFException JavaDoc
277             {
278                 long b = (long)read2n();
279                 long a = (long)read2n();
280                 return a | b << 16;
281             }
282         }
283
284
285         /**
286          * Private wrapper class for colors in RGBA float format
287          * Provides utilities to set Color from different input format
288          * And utilities to retrive them accordingly
289          */

290         //private
291
public class RGB
292         {
293             /** represents 15 bits format */
294             private static final int FORMAT_RGB5 = 0x8050;
295             /** represents 24 bits format */
296             private static final int FORMAT_RGB8 = 0x8051;
297             /** represents 16 bits format */
298             private static final int FORMAT_RGB5_A1 = 0x8057;
299             /** represents 32 bits format */
300             private static final int FORMAT_RGBA8 = 0x8058;
301
302             /** used to define color without alpha information */
303             private static final int NO_ALPHA = -1;
304
305             /** default alpha value */
306             private static final float DEFAULT_ALPHA = 1.0f;
307             /** used to define color with default format */
308             private static final float DEFAULT_FORMAT = 255.0f;
309
310             /** red value */
311             private float red;
312             /** green value */
313             private float green;
314             /** blue value */
315             private float blue;
316             /** alpha value */
317             private float alpha;
318
319             /**
320             * float gray constructor
321             * @param gray float
322             */

323             public RGB(float gray)
324             {
325                 set(gray, gray, gray, DEFAULT_ALPHA);
326             }
327
328             /**
329             * int gray constructor
330             * @param gray int
331             */

332             public RGB(int gray)
333             {
334                 set(gray, gray, gray, NO_ALPHA, NO_ALPHA);
335             }
336
337             /**
338             * int gray + alpha constructor
339             * @param gray int
340             * @param a int
341             */

342             public RGB(int gray, int a)
343             {
344                 set(gray, gray, gray, a, DEFAULT_FORMAT);
345             }
346
347             /**
348             * float gray + alpha constructor
349             * @param gray float
350             * @param a float
351             */

352             public RGB(float gray, float a)
353             {
354                 set(gray, gray, gray, a);
355             }
356
357             /**
358             * float r,g,b constructor
359             * @param r float
360             * @param g float
361             * @param b float
362             */

363             public RGB(float r, float g, float b)
364             {
365                 set(r, g, b, DEFAULT_ALPHA);
366             }
367
368             /**
369             * int r,g,b constructor
370             * @param r int
371             * @param g int
372             * @param b int
373             */

374             public RGB(int r, int g, int b)
375             {
376                 set(r, g, b, NO_ALPHA, NO_ALPHA);
377             }
378
379             /**
380             * int r,g,b + alpha constructor
381             * @param r int
382             * @param g int
383             * @param b int
384             * @param a int
385             */

386             public RGB(int r, int g, int b, int a)
387             {
388                 set((float)r / DEFAULT_FORMAT, (float)g / DEFAULT_FORMAT, (float)b / DEFAULT_FORMAT, (float) a/ DEFAULT_FORMAT);
389             }
390
391             /**
392             * float r,g,b + alpha constructor
393             * @param r float
394             * @param g float
395             * @param b float
396             * @param a float
397             */

398             public RGB(float r, float g, float b, float a)
399             {
400                 set(r, g, b, a);
401             }
402
403             /**
404             * general float r,g,b + alpha setter
405             * @param r float
406             * @param g float
407             * @param b float
408             * @param a float
409             */

410             public void set(float r, float g, float b, float a)
411             {
412                 red = r > 1.0f ? 1.0f : (r < 0.0f ? 0.0f : r);
413                 green = g > 1.0f ? 1.0f : (g < 0.0f ? 0.0f : g);
414                 blue = b > 1.0f ? 1.0f : (b < 0.0f ? 0.0f : b);
415                 alpha = a > 1.0f ? 1.0f : (a < 0.0f ? 0.0f : a);
416             }
417
418             /**
419             * general int r,g,b + alpha setter
420             * @param r int
421             * @param g int
422             * @param b int
423             * @param a int
424             * @param format float
425             */

426             public void set(int r, int g, int b, int a, float format)
427             {
428                 set((float)r / DEFAULT_FORMAT, (float)g / DEFAULT_FORMAT, (float)b / DEFAULT_FORMAT,
429                 format == NO_ALPHA ? DEFAULT_ALPHA : (float)a / format);
430             }
431
432             /**
433              * Return an int array of color packed in the requested format
434              * @param format int
435              * @return int[]
436              */

437             public int[] getPacked(int format)
438             {
439                 int r;
440                 int g;
441                 int b;
442                 switch(format)
443                 {
444                     case FORMAT_RGB5:
445                         r = (int)(red * 32.0f);
446                         g = (int)(green * 32.0f);
447                         b = (int)(blue * 32.0f);
448                         return new int[]
449                         {
450                             r + (g & 0x07) << 5,
451                             (g & 0x18) >> 3 + (b & 0x1f) << 2
452                         };
453                     case FORMAT_RGB8:
454                         return new int[]
455                         {
456                             (int)(red * 255.0f),
457                             (int)(green * 255.0f),
458                             (int)(blue * 255.0f)
459                         };
460                     case FORMAT_RGB5_A1:
461                         r = (int)(red * 32.0f);
462                         g = (int)(green * 32.0f);
463                         b = (int)(blue * 32.0f);
464                         return new int[]
465                         {
466                             r + (g & 0x07) << 5,
467                             (g & 0x18) >> 3 + (b & 0x1f) << 2 + (alpha > 0.5f ? 128 : 0)
468                         };
469                     case FORMAT_RGBA8:
470                         return new int[]
471                         {
472                             (int)(red * 255.0f),
473                             (int)(green * 255.0f),
474                             (int)(blue * 255.0f),
475                             (int)(alpha * 255.0f)
476                         };
477                     default:
478                         throw new IllegalArgumentException JavaDoc("Unknown color format "+format);
479                 }
480             }
481
482             /*
483             * get the red value as a float
484             * @return float
485             */

486             public float getRed()
487             {
488                 return red;
489             }
490
491             /*
492             * get the green value as a float
493             * @return float
494             */

495             public float getGreen()
496             {
497                 return green;
498             }
499
500             /*
501             * get the blue value as a float
502             * @return float
503             */

504             public float getBlue()
505             {
506                 return blue;
507             }
508
509             /*
510             * get the alpha value as a float
511             * @return float
512             */

513             public float getAlpha()
514             {
515                 return alpha;
516             }
517
518             /**
519             * Set the alpha value as a float
520             * @param alpha float
521             */

522             public void setAlpha(float alpha)
523             {
524                 this.alpha = alpha;
525             }
526
527             /*
528             * Compare to RGB colors (with or without alpha)
529             * @param rgb RGB the color to compare to
530             * @param compareAlpha boolean (use or not alpha in the comparison)
531             * @return boolean
532             */

533             public boolean compareTo(RGB rgb, boolean compareAlpha)
534             {
535                 if (rgb.getRed() == red && rgb.getGreen() == green && rgb.getBlue() == blue)
536                     if (!compareAlpha || (compareAlpha && rgb.getAlpha() == alpha))
537                         return true;
538                 return false;
539             }
540         }
541
542     /*
543     * Default constructor does nothing
544     * You need to use the setInputStream method to set the input
545     * before attempting to get the zlib buffer and info
546     */

547     public PNGHelper()
548     { }
549
550     /*
551     * Constructor. Allocates a Buffered Input Stream to optimises reading
552     * @param inputBuffer byte[]
553     */

554     public PNGHelper(byte[] inputBuffer)
555     {
556         setInputBuffer(inputBuffer);
557     }
558
559     /**
560      * Set the main inputStream and construct the CRCInputStream to read to
561      *
562      * @param inputBuffer buffer to read from
563      */

564     public void setInputBuffer(byte[] inputBuffer)
565     {
566         source = new CRCInputStream(new ByteArrayInputStream JavaDoc(inputBuffer));
567     }
568
569     /**
570      * Set the main inputStream and construct the CRCInputStream to read to
571      *
572      * @param fob buffer to read from
573      */

574     public void setInputBuffer(FlashBuffer fob)
575     {
576         //source = new CRCInputStream(new ByteArrayInputStream(fob.getBuf(),0,fob.getSize()));
577
source = new CRCInputStream(fob.getInputStream());
578     }
579
580     /**
581      * Return the width of the image
582      * @return width of the image
583      */

584     public int getWidth()
585     {
586         return width;
587     }
588
589     /**
590      * Return the height of the image
591      *
592      * @return height of the image
593      */

594     public int getHeight()
595     {
596         return height;
597     }
598
599     /*
600     * Return the palette size (= -1 if none)
601     * @return int
602     */

603     public int getColorTableSize()
604     {
605         return (colorsUsed -1);
606     }
607
608     /*
609     * Return transparency
610     * if colorType = 4 or 6, the image uses Alpha Channel
611     * Otherwise if a tRNS chunk has been seen
612     * @return boolean
613     */

614     public boolean hasTransparency()
615     {
616         return (colorType>3 || flagtRNS);
617     }
618
619     /*
620     * Return a color format compatible with Flash expectancy
621     * 3 = 8 bits; 4 = 16 bits; 5 = 32 bits
622     * Based on the colorType_components value
623     * And the bitDepth of image
624     * @return int
625     */

626     public int getFormat()
627     {
628         int format;
629         switch (colorType_components)
630         {
631             case 1:
632                 format = 3; // 8 bits
633
if (colorType == 0)
634                     format = 5;
635                 if (bitDepth == 16)
636                     format = 5;
637                 break;
638             //case 2:
639
//format = 4; // 16 bits : forget about it
640
//break;
641
default:
642                 format = 5; // 32 bits
643
break;
644         }
645         return format;
646     }
647
648     /*
649     * Return the zLib compressed Image data.
650     * We should be able to send back the IDAT buffer... unfortunately,
651     * its format is not recognised as such by Flash Player !
652     * So basically we need to compute our own zLib compression
653     * on data compatible with Flash
654     * This can be time consuming !
655     * @return DataMarker the zlibDatas
656     * @exception java.io.IOException
657     * @exception java.io.EOFException
658     */

659     public DataMarker getZlibData() throws IOException JavaDoc,EOFException JavaDoc,IVException
660     {
661         RGB[][] rgb = read();
662         boolean transparency = hasTransparency();
663         int format = getFormat();
664         int mult = 1;
665         switch (format)
666         {
667             case 4:
668                 mult = 2;
669                 break;
670             case 5:
671                 mult = 4;
672         }
673         byte[] tempData;
674         int plus = 3;
675         int[] pixel;
676         int idx = 0;
677         int falsewidth = width;
678         int added = 0;
679         int maxpixels = width * height;
680         // calculate padding (if needed)
681
if ((mult == 1) && (width % 4 > 0))
682         {
683             while (falsewidth % 4 > 0)
684             {
685                 falsewidth++;
686                 added++;
687             }
688             maxpixels = falsewidth * height;
689         }
690
691         if (colorType_hasPalette)
692         {
693             if (transparency) // Use RGBA palette
694
plus++;
695
696             tempData = new byte[(maxpixels) + (colorsUsed*plus)]; // Index + Palette RGB(A)
697
for (int i = 0; i < colorsUsed; i++)
698             {
699                 if (transparency)
700                     pixel = palette[i].getPacked(RGB.FORMAT_RGBA8);
701                 else
702                     pixel = palette[i].getPacked(RGB.FORMAT_RGB8);
703
704                 if (transparency && pixel[3] == 0x00) // the color itself should be 0
705
{
706                     tempData[idx++] = 0;
707                     tempData[idx++] = 0;
708                     tempData[idx++] = 0;
709                 }
710                 else
711                 {
712                     tempData[idx++] = (byte) ((pixel[0]));
713                     tempData[idx++] = (byte) ((pixel[1]));
714                     tempData[idx++] = (byte) ((pixel[2]));
715                 }
716                 if (transparency)
717                     tempData[idx++] = (byte) ((pixel[3]));
718             }
719         }
720         else
721         {
722             if (transparency)
723                 mult = 4;
724             tempData = new byte[(maxpixels * mult)]; // RGB (+ Alpha)
725
}
726
727         for (int y = 0; y < height; y++)
728         {
729             for (int x = 0; x < width; x++)
730             {
731                 if (colorType_hasPalette)
732                 {
733                     if (transparency)
734                         pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8);
735                     else
736                         pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8);
737                     int b = 0;
738                     for (int i = 0; i < palette.length; i++)
739                     {
740                         if (palette[i].compareTo(rgb[y][x],false))
741                         {
742                             b = i;
743                             break;
744                         }
745                     }
746                     tempData[idx++] = (byte) b;
747                 }
748                 else
749                 {
750                     if (mult == 4)
751                         pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8);
752                     else if (mult == 2)
753                         pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB5_A1);
754                     else
755                         pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8);
756
757                     if (pixel != null)
758                     {
759                         if (mult == 4)
760                             tempData[idx++] = (byte) ((pixel[3]));
761                         tempData[idx++] = (byte) ((pixel[0]));
762                         if (mult > 1)
763                             tempData[idx++] = (byte) ((pixel[1]));
764                         if (mult == 4)
765                             tempData[idx++] = (byte) ((pixel[2]));
766                     }
767                 }
768             }
769             if (added > 0) // 32 bits padding for 8 bits image
770
{
771                 for (int i = 0; i < added; i++)
772                 {
773                     tempData[idx++] = 0x00;
774                 }
775             }
776         }
777         Deflater JavaDoc deflater = new Deflater JavaDoc(9); // MAXIMUM Compression
778
deflater.setInput(tempData);
779         byte[] data = new byte[(maxpixels * mult) + (colorsUsed*plus)];
780         deflater.finish();
781         int defsize = deflater.deflate(data);
782         byte[] buff = new byte[defsize];
783         System.arraycopy(data,0,buff,0,defsize);
784         DataMarker zlibDatas = new DataMarker(buff,0,defsize);
785         return zlibDatas;
786     }
787
788     /***********************************************************************************
789     ** PRIVATE IMPLEMENTATION **
790     ***********************************************************************************/

791
792     /*
793     * Reads the actual datas, return a multiDimentionnal RGB array : the pixels
794     * @return RGB[][]
795     * @exception java.io.IOException
796     * @exception java.io.EOFException
797     */

798     private RGB[][] read() throws IOException JavaDoc,EOFException JavaDoc,IVException
799     {
800         if (source == null)
801             throw new IOException JavaDoc("Null input stream");
802         source.mark(Integer.MAX_VALUE);
803         buffer = new byte[0];
804         try
805         {
806             for (int i = 0; i < 8; i++)
807                 if (source.read() != PNG_SIGN[i])
808                     throw new IOException JavaDoc("Not a valid PNG file");
809
810             int length;
811             int id;
812             while(true)
813             {
814  &n