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 extends 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               Log.log(this, "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) {
163              if (Log.on) Log.log(this, "warning: tRNS chunk encountered before pLTE; ignoring alpha channel");
164              inputStream.skip(chunkLength);
165              return;
166          }
167          int len = palette.length;
168          if (colorType == 3) {
169              transparency = true;
170  
171              int transLength = len/3;
172              byte[] trans = new byte[transLength];
173              for (int i = 0; i < transLength; i++) trans[i] = (byte) 0xff;
174              inputStream.readFully(trans, 0, chunkLength);
175  
176              byte[] newPalette = new byte[len + transLength];
177              for (int i = newPalette.length; i > 0;) {
178                  newPalette[--i] = trans[--transLength];
179                  newPalette[--i] = palette[--len];
180                  newPalette[--i] = palette[--len];
181                  newPalette[--i] = palette[--len];
182              }
183              palette = newPalette;
184  
185          } else {
186              inputStream.skip(chunkLength);
187          }
188      }
189  
190      /// Helper functions for IDAT ///////////////////////////////////////////////////////////////////////////////////////////
191  
192      /** Read Image data in off of a compression stream */
193      private void readImageData() throws IOException {
194          InputStream dataStream = new SequenceInputStream(new IDATEnumeration(this));
195          DataInputStream dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(dataStream, new Inflater())));
196          intints, filterOffset;
197          switch (colorType) {
198            case 0: case 3: bps = depth; break;
199            case 2: bps = 3 * depth; break;
200            case 4: bps = depth<<1; break;
201            case 6: bps = depth<<2; break;
202            default: throw new IOException("Unknown color type encountered.");
203          }
204  
205          filterOffset = (bps + 7) >> 3;
206  
207          for (pass = (multipass ? 1 : 0); pass < 8; pass++) {
208              int pass = this.pass;
209              int rInc = rowInc[pass];
210              int cInc = colInc[pass];
211              int sCol = startingCol[pass];
212              int val = (width - sCol + cInc - 1) / cInc;
213              int samples = val * filterOffset;
214              int rowSize = (val * bps)>>3;
215              int sRow = startingRow[pass];
216              if (height <= sRow || rowSize == 0) continue;
217              int sInc = rInc * width;
218              byte inbuf[] = new byte[rowSize];
219              int pix[] = new int[rowSize];
220              int upix[] = null;
221              int temp[] = new int[rowSize];
222              int nextY = sRow;               // next Y value and number of rows to report to sendPixels
223              int rows = 0;
224              int rowStart = sRow * width;
225  
226              for (int y = sRow; y < height; y += rInc, rowStart += sInc) {
227                  rows += rInc;
228                  int rowFilter = dis.read();
229                  dis.readFully(inbuf);
230                  if (!filterRow(inbuf, pix, upix, rowFilter, filterOffset)) throw new IOException("Unknown filter type: " + rowFilter);
231                  insertPixels(pix, rowStart + sCol, samples);
232                  if (multipass && (pass < 6)) blockFill(rowStart);
233                  upix = pix;
234                  pix = temp;
235                  temp = upix;
236              }
237              if (!multipass) break;
238          }
239          while(dis.read() != -1) System.err.println("Leftover data encountered.");
240  
241          // 24-bit color is our native format
242          if (colorType == 2 || colorType == 6) {
243              data = (int[])pixels;
244              if (colorType == 2) {
245                  for(int i=0; i<data.length; i++)
246                      data[i] |= 0xFF000000;
247              }
248  
249          } else if (colorType == 3) {
250              byte[] pix = (byte[])pixels;
251              data = new int[pix.length];
252              for(int i=0; i<pix.length; i++) {
253                  if (transparency) {
254                      data[i] =
255                          ((palette[4 * (pix[i] & 0xff) + 3] & 0xff) << 24) |
256                          ((palette[4 * (pix[i] & 0xff) + 0] & 0xff) << 16) |
257                          ((palette[4 * (pix[i] & 0xff) + 1] & 0xff) << 8) |
258                          (palette[4 * (pix[i] & 0xff) + 2] & 0xff);
259                  } else {
260                      data[i] =
261                          0xFF000000 |
262                          ((palette[3 * (pix[i] & 0xff) + 0] & 0xff) << 16) |
263                          ((palette[3 * (pix[i] & 0xff) + 1] & 0xff) << 8) |
264                          (palette[3 * (pix[i] & 0xff) + 2] & 0xff);
265                  }
266              }
267  
268          } else if (colorType == 0 || colorType == 4) {
269              if (depth == 16) depth = 8;
270              int[] pix = (int[])pixels;
271              data = new int[pix.length];
272              for(int i=0; i<pix.length; i ++) {
273                  if (colorType == 0) {
274                      int val = (pix[i] & 0xff) << (8 - depth);
275                      data[i] =
276                          0xFF000000 | 
277                          (val << 16) | 
278                          (val << 8) | 
279                          val;
280                  } else {
281                      int alpha = (pix[i] & mask) << (8 - depth);
282                      int val = ((pix[i] & smask) >> depth) << (8 - depth);
283                      data[i] =
284                          (alpha << 24) |
285                          (val << 16) | 
286                          (val << 8) | 
287                          val;
288                  }
289              }
290          }
291  
292      }
293  
294      private void insertGreyPixels(int pix[], int offset, int samples) {
295          int p = pix[0];
296          int ipix[] = ipixels;
297          int cInc = colInc[pass];
298          int rs = 0;
299  
300          if (colorType == 0) {
301              switch (depth) {
302              case 1:
303                  for (int j = 0; j < samples; j++, offset += cInc) {
304                      if (rs != 0) rs--;
305                      else { rs = 7; p = pix[j>>3]; }
306                      ipix[offset] = (p>>rs) & 0x1;
307                  }
308                  break;
309              case 2:
310                  for (int j = 0; j < samples; j++, offset += cInc) {
311                      if (rs != 0) rs -= 2;
312                      else { rs = 6; p = pix[j>>2]; }
313                      ipix[offset] = (p>>rs) & 0x3;
314                  }
315                  break;
316              case 4:
317                  for (int j = 0; j < samples; j++, offset += cInc) {
318                      if (rs != 0) rs = 0;
319                      else { rs = 4; p = pix[j>>1]; }
320                      ipix[offset] = (p>>rs) & 0xf;
321                  }
322                  break;
323              case 8:
324                  for (int j = 0; j < samples; offset += cInc) ipix[offset] = (byte) pix[j++];
325                  break;
326              case 16:
327                  samples = samples<<1;
328                  for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = pix[j];
329                  break;
330              default: break;
331              }
332          } else if (colorType == 4) {
333              if (depth == 8) {
334                  for (int j = 0; j < samples; offset += cInc) ipix[offset] = (pix[j++]<<8) | pix[j++];
335              } else {
336                  samples = samples<<1;
337                  for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = (pix[j]<<8) | pix[j+=2];
338              }
339          }
340      }
341  
342      private void insertPalettedPixels(int pix[], int offset, int samples) {
343          int rs = 0;
344          int p = pix[0];
345          byte bpix[] = bpixels;
346          int cInc = colInc[pass];
347  
348          switch (depth) {
349            case 1:
350              for (int j = 0; j < samples; j++, offset += cInc) {
351                  if (rs != 0) rs--;
352                  else { rs = 7; p = pix[j>>3]; }
353                  bpix[offset] = (byte) ((p>>rs) & 0x1);
354              }
355              break;
356            case 2:
357              for (int j = 0; j < samples; j++, offset += cInc) {
358                  if (rs != 0) rs -= 2;
359                  else { rs = 6; p = pix[j>>2]; }
360                  bpix[offset] = (byte) ((p>>rs) & 0x3);
361              }
362              break;
363            case 4:
364              for (int j = 0; j < samples; j++, offset += cInc) {
365                  if (rs != 0) rs = 0;
366                  else { rs = 4; p = pix[j>>1]; }
367                  bpix[offset] = (byte) ((p>>rs) & 0xf);
368              }
369              break;
370            case 8:
371              for (int j = 0; j < samples; j++, offset += cInc) bpix[offset] = (byte) pix[j];
372              break;
373          }
374      }
375  
376      private void insertPixels(int pix[], int offset, int samples) {
377          switch (colorType) {
378          case 0:
379          case 4:
380              insertGreyPixels(pix, offset, samples);
381              break;
382          case 2: {
383              int j = 0;
384              int ipix[] = ipixels;
385              int cInc = colInc[pass];
386              if (depth == 8) {
387                  for (j = 0; j < samples; offset += cInc)
388                      ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++];
389              } else {
390                  samples = samples<<1;
391                  for (j = 0; j < samples; j += 2, offset += cInc)
392                      ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2];
393              }
394              break; }
395          case 3:
396              insertPalettedPixels(pix, offset, samples);
397              break;
398          case 6: {
399              int j = 0;
400              int ipix[] = ipixels;
401              int cInc = colInc[pass];
402              if (depth == 8) {
403                  for (j = 0; j < samples; offset += cInc) {
404                      ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++] |
405                                      (pix[j++]<<24);
406                  }
407              } else {
408                  samples = samples<<1;
409                  for (j = 0; j < samples; j += 2, offset += cInc) {
410                      ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2] |
411                                      (pix[j+=2]<<24);
412                  }
413              }
414              break; }
415            default:
416              break;
417          }
418      }
419  
420      private void blockFill(int rowStart) {
421          int counter;
422          int dw = width;
423          int pass = this.pass;
424          int w = blockWidth[pass];
425          int sCol = startingCol[pass];
426          int cInc = colInc[pass];
427          int wInc = cInc - w;
428          int maxW = rowStart + dw - w;
429          int len;
430          int h = blockHeight[pass];
431          int maxH = rowStart + (dw * h);
432          int startPos = rowStart + sCol;
433          counter = startPos;
434  
435          if (colorType == 3) {
436              byte bpix[] = bpixels;
437              byte pixel;
438              len = bpix.length;
439              for (; counter <= maxW;) {
440                  int end = counter + w;
441                  pixel = bpix[counter++];
442                  for (; counter < end; counter++) bpix[counter] = pixel;
443                  counter += wInc;
444              }
445              maxW += w;
446              if (counter < maxW)
447                  for (pixel = bpix[counter++]; counter < maxW; counter++)
448                      bpix[counter] = pixel;
449              if (len < maxH) maxH = len;
450              for (counter = startPos + dw; counter < maxH; counter += dw)
451                  System.arraycopy(bpix, startPos, bpix, counter, dw - sCol);
452          } else {
453              int ipix[] = ipixels;
454              int pixel;
455              len = ipix.length;
456              for (; counter <= maxW;) {
457                  int end = counter + w;
458                  pixel = ipix[counter++];
459                  for (; counter < end; counter++)
460                      ipix[counter] = pixel;
461                  counter += wInc;
462              }
463              maxW += w;
464              if (counter < maxW)
465                  for (pixel = ipix[counter++]; counter < maxW; counter++)
466                      ipix[counter] = pixel;
467              if (len < maxH) maxH = len;
468              for (counter = startPos + dw; counter < maxH; counter += dw)
469                  System.arraycopy(ipix, startPos, ipix, counter, dw - sCol);
470          }
471      }
472  
473      private boolean filterRow(byte inbuf[], int pix[], int upix[], int rowFilter, int boff) {
474          int rowWidth = pix.length;
475          switch (rowFilter) {
476          case 0: {
477              for (int x = 0; x < rowWidth; x++) pix[x] = 0xff & inbuf[x];
478              break; }
479          case 1: {
480              int x = 0;
481              for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
482              for ( ; x < rowWidth; x++) pix[x] = 0xff & (inbuf[x] + pix[x - boff]);
483              break; }
484          case 2: {
485              if (upix != null) {
486                  for (int x = 0; x < rowWidth; x++)
487                      pix[x] = 0xff & (upix[x] + inbuf[x]);
488              } else {
489                  for (int x = 0; x < rowWidth; x++)
490                      pix[x] = 0xff & inbuf[x];
491              }
492              break; }
493          case 3: {
494              if (upix != null) {
495                  int x = 0;
496                  for ( ; x < boff; x++) {
497                      int rval = upix[x];
498                      pix[x] = 0xff & ((rval>>1) + inbuf[x]);
499                  }
500                  for ( ; x < rowWidth; x++) {
501                      int rval = upix[x] + pix[x - boff];
502                      pix[x] = 0xff & ((rval>>1) + inbuf[x]);
503                  }
504              } else {
505                  int x = 0;
506                  for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
507                  for ( ; x < rowWidth; x++) {
508                      int rval = pix[x - boff];
509                      pix[x] = 0xff & ((rval>>1) + inbuf[x]);
510                  }
511              }
512              break; }
513          case 4: {
514              if (upix != null) {
515                  int x = 0;
516                  for ( ; x < boff; x++) pix[x] = 0xff & (upix[x] + inbuf[x]);
517                  for ( ; x < rowWidth; x++) {
518                      intintintintintintintintpc, rval;
519                      a = pix[x - boff];
520                      b = upix[x];
521                      c = upix[x - boff];
522                      p = a + b - c;
523                      pa = p > a ? p - a : a - p;
524                      pb = p > b ? p - b : b - p;
525                      pc = p > c ? p - c : c - p;
526                      if ((pa <= pb) && (pa <= pc)) rval = a;
527                      else if (pb <= pc) rval = b;
528                      else rval = c;
529                      pix[x] = 0xff & (rval + inbuf[x]);
530                  }
531              } else {
532                  int x = 0;
533                  for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
534                  for ( ; x < rowWidth; x++) {
535                      int rval = pix[x - boff];
536                      pix[x] = 0xff & (rval + inbuf[x]);
537                  }
538              }
539              break; }
540          default: return false;
541          }
542          return true;
543      }
544  
545      // Private Data ///////////////////////////////////////////////////////////////////////////////////////
546      
547      private int target_offset = 0;
548      private int width = -1;
549      private int height = -1;
550      private int sigmask = 0xffff;
551      private Object pixels = null;
552      private int ipixels[] = null;
553      private byte bpixels[] = null;
554      private boolean multipass = false;
555      private boolean complete = false;
556      private boolean error = false;
557  
558      private int[] data = null;
559  
560      private InputStream underlyingStream = null;
561      private DataInputStream inputStream = null;
562      private Thread controlThread = null;
563      private boolean infoAvailable = false;
564      private int updateDelay = 750;
565  
566      // Image decoding state variables
567      private boolean headerFound = false;
568      private int compressionMethod = -1;
569      private int depth = -1;
570      private int colorType = -1;
571      private int filterMethod = -1;
572      private int interlaceMethod = -1;
573      private int pass = 0;
574      private byte palette[] = null;
575      private int mask = 0x0;
576      private int smask = 0x0;
577      private boolean transparency = false;
578  
579      private int chunkLength = 0;
580      private int chunkType = 0;
581      private boolean needChunkInfo = true;
582  
583      private static final int CHUNK_bKGD = 0x624B4744;   // "bKGD"
584      private static final int CHUNK_cHRM = 0x6348524D;   // "cHRM"
585      private static final int CHUNK_gAMA = 0x67414D41;   // "gAMA"
586      private static final int CHUNK_hIST = 0x68495354;   // "hIST"
587      private static final int CHUNK_IDAT = 0x49444154;   // "IDAT"
588      private static final int CHUNK_IEND = 0x49454E44;   // "IEND"
589      private static final int CHUNK_IHDR = 0x49484452;   // "IHDR"
590      private static final int CHUNK_PLTE = 0x504C5445;   // "PLTE"
591      private static final int CHUNK_pHYs = 0x70485973;   // "pHYs"
592      private static final int CHUNK_sBIT = 0x73424954;   // "sBIT"
593      private static final int CHUNK_tEXt = 0x74455874;   // "tEXt"
594      private static final int CHUNK_tIME = 0x74494D45;   // "tIME"
595      private static final int CHUNK_tRNS = 0x74524E53;   // "tIME"
596      private static final int CHUNK_zTXt = 0x7A545874;   // "zTXt"
597  
598      private static final int startingRow[]  =  { 0, 0, 0, 4, 0, 2, 0, 1 };
599      private static final int startingCol[]  =  { 0, 0, 4, 0, 2, 0, 1, 0 };
600      private static final int rowInc[]       =  { 1, 8, 8, 8, 4, 4, 2, 2 };
601      private static final int colInc[]       =  { 1, 8, 8, 4, 4, 2, 2, 1 };
602      private static final int blockHeight[]  =  { 1, 8, 8, 4, 4, 2, 2, 1 };
603      private static final int blockWidth[]   =  { 1, 8, 4, 4, 2, 2, 1, 1 };
604  
605      // Helper Classes ////////////////////////////////////////////////////////////////////
606  
607      private static class MeteredInputStream extends FilterInputStream {
608          int bytesLeft;
609          int marked;
610          
611          public MeteredInputStream(InputStream in, int size) {
612              super(in);
613              bytesLeft = size;
614          }
615          
616          public final int read() throws IOException {
617              if (bytesLeft > 0) {
618                  int val = in.read();
619                  if (val != -1) bytesLeft--;
620                  return val;
621              }
622              return -1;
623          }
624          
625          public final int read(byte b[]) throws IOException {
626              return read(b, 0, b.length);
627          }
628          
629          public final int read(byte b[], int off, int len) throws IOException {
630              if (bytesLeft > 0) {
631                  len = (len > bytesLeft ? bytesLeft : len);
632                  int read = in.read(b, off, len);
633                  if (read > 0) bytesLeft -= read;
634                  return read;
635              }
636              return -1;
637          }
638          
639          public final long skip(long n) throws IOException {
640              n = (n > bytesLeft ? bytesLeft : n);
641              long skipped = in.skip(n);
642              if (skipped > 0) bytesLeft -= skipped;
643              return skipped;
644          }
645          
646          public final int available() throws IOException {
647              int n = in.available();
648              return (n > bytesLeft ? bytesLeft : n);
649          }
650          
651          public final void close() throws IOException { /* Eat this */ }
652  
653          public final void mark(int readlimit) {
654              marked = bytesLeft;
655              in.mark(readlimit);
656          }
657          
658          public final void reset() throws IOException {
659              in.reset();
660              bytesLeft = marked;
661          }
662          
663          public final boolean markSupported() { return in.markSupported(); }
664      }
665  
666      /** Support class, used to eat the IDAT headers dividing up the deflated stream */
667      private static class IDATEnumeration implements Enumeration {
668          InputStream underlyingStream;
669          PNG owner;
670          boolean firstStream = true;
671          
672          public IDATEnumeration(PNG owner) {
673              this.owner = owner;
674              this.underlyingStream = owner.underlyingStream;
675          }
676          
677          public Object nextElement() {
678              firstStream = false;
679              return new MeteredInputStream(underlyingStream, owner.chunkLength);
680          }
681          
682          public boolean hasMoreElements() {
683              DataInputStream dis = new DataInputStream(underlyingStream);
684              if (!firstStream) {
685                  try {
686                      int crc = dis.readInt();
687                      owner.needChunkInfo = false;
688                      owner.chunkLength = dis.readInt();
689                      owner.chunkType = dis.readInt();
690                  } catch (IOException ioe) {
691                      return false;
692                  }
693              }
694              if (owner.chunkType == PNG.CHUNK_IDAT) return true;
695              return false;
696          }
697      }
698  
699  }
700