1c06968bb954cb53cfb6e5cfaae99db6d57bb7f5
[org.ibex.core.git] / src / org / xwt / PNG.java
1 /*
2  * This file was adapted from Jason Marshall's PNGImageProducer.java
3  *
4  * Copyright (c) 1997, Jason Marshall.  All Rights Reserved
5  *
6  * The author makes no representations or warranties regarding the suitability,
7  * reliability or stability of this code.  This code is provided AS IS.  The
8  * author shall not be liable for any damages suffered as a result of using,
9  * modifying or redistributing this software or any derivitives thereof.
10  * Permission to use, reproduce, modify and/or (re)distribute this software is
11  * hereby granted.
12  */
13
14 package org.xwt;
15
16 import org.xwt.util.*;
17 import java.io.*;
18 import java.util.Hashtable;
19 import java.util.Vector;
20 import java.util.Enumeration;
21 import java.util.zip.*;
22
23 /** Converts an InputStream carrying a PNG image into an ARGB int[] */
24 public class PNG implements ImageDecoder {
25
26     // Public Methods ///////////////////////////////////////////////////////////////////////////////
27
28     /** returns the ARGB int[] representing the last image processed */
29     public final int[] getData() { return data; }
30
31     /** returns the width of the last image processed */
32     public final int getWidth() { return width; }
33
34     /** returns the height of the last image processed */
35     public final int getHeight() { return height; }
36
37     /** process a PNG as an inputstream; returns null if there is an error
38         @param name A string describing the image, to be used when logging errors
39     */
40     public static PNG decode(InputStream is, String name) {
41         try {
42             return new PNG(is, name);
43         } catch (Exception e) {
44             if (Log.on) Log.log(PNG.class, e);
45             return null;
46         }
47     }
48
49     private PNG(InputStream is, String name) throws IOException {
50         underlyingStream = is;
51         target_offset = 0;
52         inputStream = new DataInputStream(underlyingStream);
53         
54         // consume the header
55         if ((inputStream.read() != 137) || (inputStream.read() != 80) || (inputStream.read() != 78) || (inputStream.read() != 71) ||
56             (inputStream.read() != 13) || (inputStream.read() != 10) || (inputStream.read() != 26) || (inputStream.read() != 10)) {
57             System.out.println("PNG: error: input file " + name + " is not a PNG file");
58             data = new int[] { };
59             width = height = 0;
60             return;
61         }
62         
63         DONE: while (!error) {
64             if (needChunkInfo) {
65                 chunkLength = inputStream.readInt();
66                 chunkType = inputStream.readInt();
67                 needChunkInfo = false;
68             }
69             
70             switch (chunkType) {
71             case CHUNK_bKGD: inputStream.skip(chunkLength); break;
72             case CHUNK_cHRM: inputStream.skip(chunkLength); break;
73             case CHUNK_gAMA: inputStream.skip(chunkLength); break;
74             case CHUNK_hIST: inputStream.skip(chunkLength); break;
75             case CHUNK_pHYs: inputStream.skip(chunkLength); break;
76             case CHUNK_sBIT: inputStream.skip(chunkLength); break;
77             case CHUNK_tEXt: inputStream.skip(chunkLength); break;
78             case CHUNK_zTXt: inputStream.skip(chunkLength); break;
79             case CHUNK_tIME: inputStream.skip(chunkLength); break;
80                 
81             case CHUNK_IHDR: handleIHDR(); break;
82             case CHUNK_PLTE: handlePLTE(); break;
83             case CHUNK_tRNS: handletRNS(); break;
84                 
85             case CHUNK_IDAT: handleIDAT(); break;
86                 
87             case CHUNK_IEND: break DONE;
88             default:
89                 System.err.println("unrecognized chunk type " +
90                                    Integer.toHexString(chunkType) + ". skipping");
91                 inputStream.skip(chunkLength);
92             }
93             
94             int crc = inputStream.readInt();
95             needChunkInfo = true;
96         }
97     }
98
99     // Chunk Handlers ///////////////////////////////////////////////////////////////////////
100
101     /** handle data chunk */
102     private void handleIDAT() throws IOException {
103         if (width == -1 || height == -1) throw new IOException("never got image width/height");
104         switch (depth) {
105         case 1: mask = 0x1; break;
106         case 2: mask = 0x3; break;
107         case 4: mask = 0xf; break;
108         case 8: case 16: mask = 0xff; break;
109         default: mask = 0x0; break;
110         }
111         if (depth < 8) smask = mask << depth;
112         else smask = mask << 8;
113
114         int count = width * height;
115
116         switch (colorType) {
117         case 0:
118         case 2:
119         case 6:
120         case 4:
121             ipixels = new int[count];
122             pixels = ipixels;
123             break;
124         case 3:
125             bpixels = new byte[count];
126             pixels = bpixels;
127             break;
128         default:
129             throw new IOException("Image has unknown color type");
130         }
131         if (interlaceMethod != 0) multipass = true;
132         readImageData();
133     }
134
135     /** handle header chunk */
136     private void handleIHDR() throws IOException {
137         if (headerFound) throw new IOException("Extraneous IHDR chunk encountered.");
138         if (chunkLength != 13) throw new IOException("IHDR chunk length wrong: " + chunkLength);
139         width = inputStream.readInt();
140         height = inputStream.readInt();
141         depth = inputStream.read();
142         colorType = inputStream.read();
143         compressionMethod = inputStream.read();
144         filterMethod = inputStream.read();
145         interlaceMethod = inputStream.read();
146     }
147
148     /** handle pallette chunk */
149     private void handlePLTE() throws IOException {
150         if (colorType == 3) {
151             palette = new byte[chunkLength];
152             inputStream.readFully(palette);
153         } else {
154             // Ignore suggested palette
155             inputStream.skip(chunkLength);
156         }
157     }
158
159     /** handle transparency chunk; modifies palette */
160     private void handletRNS() throws IOException {
161         int chunkLen = chunkLength;
162                 if (palette == null) throw new IOException("tRNS chunk encountered before pLTE");
163                 int len = palette.length;
164         if (colorType == 3) {
165             transparency = true;
166
167             int transLength = len/3;
168             byte[] trans = new byte[transLength];
169             for (int i = 0; i < transLength; i++) trans[i] = (byte) 0xff;
170             inputStream.readFully(trans, 0, chunkLength);
171
172             byte[] newPalette = new byte[len + transLength];
173             for (int i = newPalette.length; i > 0;) {
174                 newPalette[--i] = trans[--transLength];
175                 newPalette[--i] = palette[--len];
176                 newPalette[--i] = palette[--len];
177                 newPalette[--i] = palette[--len];
178             }
179             palette = newPalette;
180
181         } else {
182             inputStream.skip(chunkLength);
183         }
184     }
185
186     /// Helper functions for IDAT ///////////////////////////////////////////////////////////////////////////////////////////
187
188     /** Read Image data in off of a compression stream */
189     private void readImageData() throws IOException {
190         InputStream dataStream = new SequenceInputStream(new IDATEnumeration(this));
191         DataInputStream dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(dataStream, new Inflater())));
192         int bps, filterOffset;
193         switch (colorType) {
194           case 0: case 3: bps = depth; break;
195           case 2: bps = 3 * depth; break;
196           case 4: bps = depth<<1; break;
197           case 6: bps = depth<<2; break;
198           default: throw new IOException("Unknown color type encountered.");
199         }
200
201         filterOffset = (bps + 7) >> 3;
202
203         for (pass = (multipass ? 1 : 0); pass < 8; pass++) {
204             int pass = this.pass;
205             int rInc = rowInc[pass];
206             int cInc = colInc[pass];
207             int sCol = startingCol[pass];
208             int val = (width - sCol + cInc - 1) / cInc;
209             int samples = val * filterOffset;
210             int rowSize = (val * bps)>>3;
211             int sRow = startingRow[pass];
212             if (height <= sRow || rowSize == 0) continue;
213             int sInc = rInc * width;
214             byte inbuf[] = new byte[rowSize];
215             int pix[] = new int[rowSize];
216             int upix[] = null;
217             int temp[] = new int[rowSize];
218             int nextY = sRow;               // next Y value and number of rows to report to sendPixels
219             int rows = 0;
220             int rowStart = sRow * width;
221
222             for (int y = sRow; y < height; y += rInc, rowStart += sInc) {
223                 rows += rInc;
224                 int rowFilter = dis.read();
225                 dis.readFully(inbuf);
226                 if (!filterRow(inbuf, pix, upix, rowFilter, filterOffset)) throw new IOException("Unknown filter type: " + rowFilter);
227                 insertPixels(pix, rowStart + sCol, samples);
228                 if (multipass && (pass < 6)) blockFill(rowStart);
229                 upix = pix;
230                 pix = temp;
231                 temp = upix;
232             }
233             if (!multipass) break;
234         }
235         while(dis.read() != -1) System.err.println("Leftover data encountered.");
236
237         // 24-bit color is our native format
238         if (colorType == 2 || colorType == 6) {
239             data = (int[])pixels;
240             if (colorType == 2) {
241                 for(int i=0; i<data.length; i++)
242                     data[i] |= 0xFF000000;
243             }
244
245         } else if (colorType == 3) {
246             byte[] pix = (byte[])pixels;
247             data = new int[pix.length];
248             for(int i=0; i<pix.length; i++) {
249                 if (transparency) {
250                     data[i] =
251                         ((palette[4 * (pix[i] & 0xff) + 3] & 0xff) << 24) |
252                         ((palette[4 * (pix[i] & 0xff) + 0] & 0xff) << 16) |
253                         ((palette[4 * (pix[i] & 0xff) + 1] & 0xff) << 8) |
254                         (palette[4 * (pix[i] & 0xff) + 2] & 0xff);
255                 } else {
256                     data[i] =
257                         0xFF000000 |
258                         ((palette[3 * (pix[i] & 0xff) + 0] & 0xff) << 16) |
259                         ((palette[3 * (pix[i] & 0xff) + 1] & 0xff) << 8) |
260                         (palette[3 * (pix[i] & 0xff) + 2] & 0xff);
261                 }
262             }
263
264         } else if (colorType == 0 || colorType == 4) {
265             if (depth == 16) depth = 8;
266             int[] pix = (int[])pixels;
267             data = new int[pix.length];
268             for(int i=0; i<pix.length; i ++) {
269                 if (colorType == 0) {
270                     int val = (pix[i] & 0xff) << (8 - depth);
271                     data[i] =
272                         0xFF000000 | 
273                         (val << 16) | 
274                         (val << 8) | 
275                         val;
276                 } else {
277                     int alpha = (pix[i] & mask) << (8 - depth);
278                     int val = ((pix[i] & smask) >> depth) << (8 - depth);
279                     data[i] =
280                         (alpha << 24) |
281                         (val << 16) | 
282                         (val << 8) | 
283                         val;
284                 }
285             }
286         }
287
288     }
289
290     private void insertGreyPixels(int pix[], int offset, int samples) {
291         int p = pix[0];
292         int ipix[] = ipixels;
293         int cInc = colInc[pass];
294         int rs = 0;
295
296         if (colorType == 0) {
297             switch (depth) {
298             case 1:
299                 for (int j = 0; j < samples; j++, offset += cInc) {
300                     if (rs != 0) rs--;
301                     else { rs = 7; p = pix[j>>3]; }
302                     ipix[offset] = (p>>rs) & 0x1;
303                 }
304                 break;
305             case 2:
306                 for (int j = 0; j < samples; j++, offset += cInc) {
307                     if (rs != 0) rs -= 2;
308                     else { rs = 6; p = pix[j>>2]; }
309                     ipix[offset] = (p>>rs) & 0x3;
310                 }
311                 break;
312             case 4:
313                 for (int j = 0; j < samples; j++, offset += cInc) {
314                     if (rs != 0) rs = 0;
315                     else { rs = 4; p = pix[j>>1]; }
316                     ipix[offset] = (p>>rs) & 0xf;
317                 }
318                 break;
319             case 8:
320                 for (int j = 0; j < samples; offset += cInc) ipix[offset] = (byte) pix[j++];
321                 break;
322             case 16:
323                 samples = samples<<1;
324                 for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = pix[j];
325                 break;
326             default: break;
327             }
328         } else if (colorType == 4) {
329             if (depth == 8) {
330                 for (int j = 0; j < samples; offset += cInc) ipix[offset] = (pix[j++]<<8) | pix[j++];
331             } else {
332                 samples = samples<<1;
333                 for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = (pix[j]<<8) | pix[j+=2];
334             }
335         }
336     }
337
338     private void insertPalettedPixels(int pix[], int offset, int samples) {
339         int rs = 0;
340         int p = pix[0];
341         byte bpix[] = bpixels;
342         int cInc = colInc[pass];
343
344         switch (depth) {
345           case 1:
346             for (int j = 0; j < samples; j++, offset += cInc) {
347                 if (rs != 0) rs--;
348                 else { rs = 7; p = pix[j>>3]; }
349                 bpix[offset] = (byte) ((p>>rs) & 0x1);
350             }
351             break;
352           case 2:
353             for (int j = 0; j < samples; j++, offset += cInc) {
354                 if (rs != 0) rs -= 2;
355                 else { rs = 6; p = pix[j>>2]; }
356                 bpix[offset] = (byte) ((p>>rs) & 0x3);
357             }
358             break;
359           case 4:
360             for (int j = 0; j < samples; j++, offset += cInc) {
361                 if (rs != 0) rs = 0;
362                 else { rs = 4; p = pix[j>>1]; }
363                 bpix[offset] = (byte) ((p>>rs) & 0xf);
364             }
365             break;
366           case 8:
367             for (int j = 0; j < samples; j++, offset += cInc) bpix[offset] = (byte) pix[j];
368             break;
369         }
370     }
371
372     private void insertPixels(int pix[], int offset, int samples) {
373         switch (colorType) {
374         case 0:
375         case 4:
376             insertGreyPixels(pix, offset, samples);
377             break;
378         case 2: {
379             int j = 0;
380             int ipix[] = ipixels;
381             int cInc = colInc[pass];
382             if (depth == 8) {
383                 for (j = 0; j < samples; offset += cInc)
384                     ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++];
385             } else {
386                 samples = samples<<1;
387                 for (j = 0; j < samples; j += 2, offset += cInc)
388                     ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2];
389             }
390             break; }
391         case 3:
392             insertPalettedPixels(pix, offset, samples);
393             break;
394         case 6: {
395             int j = 0;
396             int ipix[] = ipixels;
397             int cInc = colInc[pass];
398             if (depth == 8) {
399                 for (j = 0; j < samples; offset += cInc) {
400                     ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++] |
401                                     (pix[j++]<<24);
402                 }
403             } else {
404                 samples = samples<<1;
405                 for (j = 0; j < samples; j += 2, offset += cInc) {
406                     ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2] |
407                                     (pix[j+=2]<<24);
408                 }
409             }
410             break; }
411           default:
412             break;
413         }
414     }
415
416     private void blockFill(int rowStart) {
417         int counter;
418         int dw = width;
419         int pass = this.pass;
420         int w = blockWidth[pass];
421         int sCol = startingCol[pass];
422         int cInc = colInc[pass];
423         int wInc = cInc - w;
424         int maxW = rowStart + dw - w;
425         int len;
426         int h = blockHeight[pass];
427         int maxH = rowStart + (dw * h);
428         int startPos = rowStart + sCol;
429         counter = startPos;
430
431         if (colorType == 3) {
432             byte bpix[] = bpixels;
433             byte pixel;
434             len = bpix.length;
435             for (; counter <= maxW;) {
436                 int end = counter + w;
437                 pixel = bpix[counter++];
438                 for (; counter < end; counter++) bpix[counter] = pixel;
439                 counter += wInc;
440             }
441             maxW += w;
442             if (counter < maxW)
443                 for (pixel = bpix[counter++]; counter < maxW; counter++)
444                     bpix[counter] = pixel;
445             if (len < maxH) maxH = len;
446             for (counter = startPos + dw; counter < maxH; counter += dw)
447                 System.arraycopy(bpix, startPos, bpix, counter, dw - sCol);
448         } else {
449             int ipix[] = ipixels;
450             int pixel;
451             len = ipix.length;
452             for (; counter <= maxW;) {
453                 int end = counter + w;
454                 pixel = ipix[counter++];
455                 for (; counter < end; counter++)
456                     ipix[counter] = pixel;
457                 counter += wInc;
458             }
459             maxW += w;
460             if (counter < maxW)
461                 for (pixel = ipix[counter++]; counter < maxW; counter++)
462                     ipix[counter] = pixel;
463             if (len < maxH) maxH = len;
464             for (counter = startPos + dw; counter < maxH; counter += dw)
465                 System.arraycopy(ipix, startPos, ipix, counter, dw - sCol);
466         }
467     }
468
469     private boolean filterRow(byte inbuf[], int pix[], int upix[], int rowFilter, int boff) {
470         int rowWidth = pix.length;
471         switch (rowFilter) {
472         case 0: {
473             for (int x = 0; x < rowWidth; x++) pix[x] = 0xff & inbuf[x];
474             break; }
475         case 1: {
476             int x = 0;
477             for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
478             for ( ; x < rowWidth; x++) pix[x] = 0xff & (inbuf[x] + pix[x - boff]);
479             break; }
480         case 2: {
481             if (upix != null) {
482                 for (int x = 0; x < rowWidth; x++)
483                     pix[x] = 0xff & (upix[x] + inbuf[x]);
484             } else {
485                 for (int x = 0; x < rowWidth; x++)
486                     pix[x] = 0xff & inbuf[x];
487             }
488             break; }
489         case 3: {
490             if (upix != null) {
491                 int x = 0;
492                 for ( ; x < boff; x++) {
493                     int rval = upix[x];
494                     pix[x] = 0xff & ((rval>>1) + inbuf[x]);
495                 }
496                 for ( ; x < rowWidth; x++) {
497                     int rval = upix[x] + pix[x - boff];
498                     pix[x] = 0xff & ((rval>>1) + inbuf[x]);
499                 }
500             } else {
501                 int x = 0;
502                 for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
503                 for ( ; x < rowWidth; x++) {
504                     int rval = pix[x - boff];
505                     pix[x] = 0xff & ((rval>>1) + inbuf[x]);
506                 }
507             }
508             break; }
509         case 4: {
510             if (upix != null) {
511                 int x = 0;
512                 for ( ; x < boff; x++) pix[x] = 0xff & (upix[x] + inbuf[x]);
513                 for ( ; x < rowWidth; x++) {
514                     int a, b, c, p, pa, pb, pc, rval;
515                     a = pix[x - boff];
516                     b = upix[x];
517                     c = upix[x - boff];
518                     p = a + b - c;
519                     pa = p > a ? p - a : a - p;
520                     pb = p > b ? p - b : b - p;
521                     pc = p > c ? p - c : c - p;
522                     if ((pa <= pb) && (pa <= pc)) rval = a;
523                     else if (pb <= pc) rval = b;
524                     else rval = c;
525                     pix[x] = 0xff & (rval + inbuf[x]);
526                 }
527             } else {
528                 int x = 0;
529                 for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
530                 for ( ; x < rowWidth; x++) {
531                     int rval = pix[x - boff];
532                     pix[x] = 0xff & (rval + inbuf[x]);
533                 }
534             }
535             break; }
536         default: return false;
537         }
538         return true;
539     }
540
541     // Private Data ///////////////////////////////////////////////////////////////////////////////////////
542     
543     private int target_offset = 0;
544     private int width = -1;
545     private int height = -1;
546     private int sigmask = 0xffff;
547     private Object pixels = null;
548     private int ipixels[] = null;
549     private byte bpixels[] = null;
550     private boolean multipass = false;
551     private boolean complete = false;
552     private boolean error = false;
553
554     private int[] data = null;
555
556     private InputStream underlyingStream = null;
557     private DataInputStream inputStream = null;
558     private Thread controlThread = null;
559     private boolean infoAvailable = false;
560     private int updateDelay = 750;
561
562     // Image decoding state variables
563     private boolean headerFound = false;
564     private int compressionMethod = -1;
565     private int depth = -1;
566     private int colorType = -1;
567     private int filterMethod = -1;
568     private int interlaceMethod = -1;
569     private int pass = 0;
570     private byte palette[] = null;
571     private int mask = 0x0;
572     private int smask = 0x0;
573     private boolean transparency = false;
574
575     private int chunkLength = 0;
576     private int chunkType = 0;
577     private boolean needChunkInfo = true;
578
579     private static final int CHUNK_bKGD = 0x624B4744;   // "bKGD"
580     private static final int CHUNK_cHRM = 0x6348524D;   // "cHRM"
581     private static final int CHUNK_gAMA = 0x67414D41;   // "gAMA"
582     private static final int CHUNK_hIST = 0x68495354;   // "hIST"
583     private static final int CHUNK_IDAT = 0x49444154;   // "IDAT"
584     private static final int CHUNK_IEND = 0x49454E44;   // "IEND"
585     private static final int CHUNK_IHDR = 0x49484452;   // "IHDR"
586     private static final int CHUNK_PLTE = 0x504C5445;   // "PLTE"
587     private static final int CHUNK_pHYs = 0x70485973;   // "pHYs"
588     private static final int CHUNK_sBIT = 0x73424954;   // "sBIT"
589     private static final int CHUNK_tEXt = 0x74455874;   // "tEXt"
590     private static final int CHUNK_tIME = 0x74494D45;   // "tIME"
591     private static final int CHUNK_tRNS = 0x74524E53;   // "tIME"
592     private static final int CHUNK_zTXt = 0x7A545874;   // "zTXt"
593
594     private static final int startingRow[]  =  { 0, 0, 0, 4, 0, 2, 0, 1 };
595     private static final int startingCol[]  =  { 0, 0, 4, 0, 2, 0, 1, 0 };
596     private static final int rowInc[]       =  { 1, 8, 8, 8, 4, 4, 2, 2 };
597     private static final int colInc[]       =  { 1, 8, 8, 4, 4, 2, 2, 1 };
598     private static final int blockHeight[]  =  { 1, 8, 8, 4, 4, 2, 2, 1 };
599     private static final int blockWidth[]   =  { 1, 8, 4, 4, 2, 2, 1, 1 };
600
601     // Helper Classes ////////////////////////////////////////////////////////////////////
602
603     private static class MeteredInputStream extends FilterInputStream {
604         int bytesLeft;
605         int marked;
606         
607         public MeteredInputStream(InputStream in, int size) {
608             super(in);
609             bytesLeft = size;
610         }
611         
612         public final int read() throws IOException {
613             if (bytesLeft > 0) {
614                 int val = in.read();
615                 if (val != -1) bytesLeft--;
616                 return val;
617             }
618             return -1;
619         }
620         
621         public final int read(byte b[]) throws IOException {
622             return read(b, 0, b.length);
623         }
624         
625         public final int read(byte b[], int off, int len) throws IOException {
626             if (bytesLeft > 0) {
627                 len = (len > bytesLeft ? bytesLeft : len);
628                 int read = in.read(b, off, len);
629                 if (read > 0) bytesLeft -= read;
630                 return read;
631             }
632             return -1;
633         }
634         
635         public final long skip(long n) throws IOException {
636             n = (n > bytesLeft ? bytesLeft : n);
637             long skipped = in.skip(n);
638             if (skipped > 0) bytesLeft -= skipped;
639             return skipped;
640         }
641         
642         public final int available() throws IOException {
643             int n = in.available();
644             return (n > bytesLeft ? bytesLeft : n);
645         }
646         
647         public final void close() throws IOException { /* Eat this */ }
648
649         public final void mark(int readlimit) {
650             marked = bytesLeft;
651             in.mark(readlimit);
652         }
653         
654         public final void reset() throws IOException {
655             in.reset();
656             bytesLeft = marked;
657         }
658         
659         public final boolean markSupported() { return in.markSupported(); }
660     }
661
662     /** Support class, used to eat the IDAT headers dividing up the deflated stream */
663     private static class IDATEnumeration implements Enumeration {
664         InputStream underlyingStream;
665         PNG owner;
666         boolean firstStream = true;
667         
668         public IDATEnumeration(PNG owner) {
669             this.owner = owner;
670             this.underlyingStream = owner.underlyingStream;
671         }
672         
673         public Object nextElement() {
674             firstStream = false;
675             return new MeteredInputStream(underlyingStream, owner.chunkLength);
676         }
677         
678         public boolean hasMoreElements() {
679             DataInputStream dis = new DataInputStream(underlyingStream);
680             if (!firstStream) {
681                 try {
682                     int crc = dis.readInt();
683                     owner.needChunkInfo = false;
684                     owner.chunkLength = dis.readInt();
685                     owner.chunkType = dis.readInt();
686                 } catch (IOException ioe) {
687                     return false;
688                 }
689             }
690             if (owner.chunkType == PNG.CHUNK_IDAT) return true;
691             return false;
692         }
693     }
694
695 }