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