1/*
2 * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.awt.image;
27
28import java.io.*;
29import java.util.*;
30import java.util.zip.*;
31import java.awt.image.*;
32import java.awt.Color;
33
34/** PNG - Portable Network Graphics - image file reader.
35    See <a href=http://www.ietf.org/rfc/rfc2083.txt>RFC2083</a> for details. */
36
37/* this is changed
38public class PNGImageDecoder extends FilterInputStream implements Runnable
39{ */
40
41public class PNGImageDecoder extends ImageDecoder
42{
43    private static final int GRAY=0;
44    private static final int PALETTE=1;
45    private static final int COLOR=2;
46    private static final int ALPHA=4;
47
48    private static final int bKGDChunk = 0x624B4744;
49    private static final int cHRMChunk = 0x6348524D;
50    private static final int gAMAChunk = 0x67414D41;
51    private static final int hISTChunk = 0x68495354;
52    private static final int IDATChunk = 0x49444154;
53    private static final int IENDChunk = 0x49454E44;
54    private static final int IHDRChunk = 0x49484452;
55    private static final int PLTEChunk = 0x504C5445;
56    private static final int pHYsChunk = 0x70485973;
57    private static final int sBITChunk = 0x73424954;
58    private static final int tEXtChunk = 0x74455874;
59    private static final int tIMEChunk = 0x74494D45;
60    private static final int tRNSChunk = 0x74524E53;
61    private static final int zTXtChunk = 0x7A545874;
62
63    private int width;
64    private int height;
65    private int bitDepth;
66    private int colorType;
67    private int compressionMethod;
68    private int filterMethod;
69    private int interlaceMethod;
70    private int gamma = 100000;
71    private java.util.Hashtable<String, Object> properties;
72  /* this is not needed
73    ImageConsumer target;
74    */
75    private ColorModel cm;
76    private byte[] red_map, green_map, blue_map, alpha_map;
77    private int transparentPixel = -1;
78    private byte[]  transparentPixel_16 = null; // we need 6 bytes to store 16bpp value
79    private static ColorModel greyModels[] = new ColorModel[4];
80  /* this is not needed
81     PNGImageDecoder next;
82     */
83
84    private void property(String key,Object value) {
85        if(value==null) return;
86        if(properties==null) properties=new java.util.Hashtable<>();
87        properties.put(key,value);
88    }
89    private void property(String key,float value) {
90        property(key, Float.valueOf(value));
91    }
92    private final void pngassert(boolean b) throws IOException {
93        if(!b) {
94            PNGException e = new PNGException("Broken file");
95            e.printStackTrace();
96            throw e;
97        }
98    }
99    protected boolean handleChunk(int key, byte[] buf, int st, int len)
100        throws IOException {
101        switch(key) {
102            case bKGDChunk:
103                Color c = null;
104                switch(colorType) {
105                    case COLOR:
106                    case COLOR|ALPHA:
107                        pngassert(len==6);
108                        c = new Color(buf[st]&0xff,buf[st+2]&0xff,buf[st+4]&0xff);
109                        break;
110                    case COLOR|PALETTE:
111                    case COLOR|PALETTE|ALPHA:
112                        pngassert(len==1);
113                        int ix = buf[st]&0xFF;
114                        pngassert(red_map!=null && ix<red_map.length);
115                        c = new Color(red_map[ix]&0xff,green_map[ix]&0xff,blue_map[ix]&0xff);
116                        break;
117                    case GRAY:
118                    case GRAY|ALPHA:
119                        pngassert(len==2);
120                        int t = buf[st]&0xFF;
121                        c = new Color(t,t,t);
122                        break;
123                }
124                if(c!=null) property("background",c);
125                break;
126            case cHRMChunk:
127                property("chromaticities",
128                    new Chromaticities(
129                        getInt(st),
130                        getInt(st+4),
131                        getInt(st+8),
132                        getInt(st+12),
133                        getInt(st+16),
134                        getInt(st+20),
135                        getInt(st+24),
136                        getInt(st+28)));
137                break;
138            case gAMAChunk:
139                if(len!=4) throw new PNGException("bogus gAMA");
140                gamma = getInt(st);
141                if(gamma!=100000) property("gamma",gamma/100000.0f);
142                break;
143            case hISTChunk: break;
144            case IDATChunk: return false;
145            case IENDChunk: break;
146            case IHDRChunk:
147                if(len!=13
148                    ||(width = getInt(st))==0
149                    ||(height = getInt(st+4))==0
150                    ) throw new PNGException("bogus IHDR");
151                bitDepth = getByte(st+8);
152                colorType = getByte(st+9);
153                compressionMethod = getByte(st+10);
154                filterMethod = getByte(st+11);
155                interlaceMethod = getByte(st+12);
156                /* this is not needed
157                  if(target!=null) target.setDimensions(width,height);
158                  */
159                break;
160            case PLTEChunk:
161                {   int tsize = len/3;
162                    red_map = new byte[tsize];
163                    green_map = new byte[tsize];
164                    blue_map = new byte[tsize];
165                    for(int i=0,j=st; i<tsize; i++, j+=3) {
166                        red_map[i] = buf[j];
167                        green_map[i] = buf[j+1];
168                        blue_map[i] = buf[j+2];
169                    }
170                }
171                break;
172            case pHYsChunk: break;
173            case sBITChunk: break;
174            case tEXtChunk:
175                int klen = 0;
176                while(klen<len && buf[st+klen]!=0) klen++;
177                if(klen<len) {
178                    String tkey = new String(buf,st,klen);
179                    String tvalue = new String(buf,st+klen+1,len-klen-1);
180                    property(tkey,tvalue);
181                }
182                break;
183            case tIMEChunk:
184                property("modtime",new GregorianCalendar(
185                    getShort(st+0),
186                    getByte(st+2)-1,
187                    getByte(st+3),
188                    getByte(st+4),
189                    getByte(st+5),
190                    getByte(st+6)).getTime());
191                break;
192            case tRNSChunk:
193                switch(colorType) {
194                    case PALETTE|COLOR:
195                    case PALETTE|COLOR|ALPHA:
196                        int alen = len;
197                        if(red_map!=null) alen = red_map.length;
198                        alpha_map = new byte[alen];
199                        System.arraycopy(buf,st,alpha_map,0,len<alen ? len : alen);
200                        while (--alen>=len) alpha_map[alen] = (byte)0xFF;
201                        break;
202                    case COLOR: // doesn't deal with 16 bit colors properly
203                    case COLOR|ALPHA: // doesn't deal with 16 bit colors properly
204                        pngassert(len==6);
205                        if (bitDepth == 16) {
206                            transparentPixel_16 = new byte[6];
207                            for (int i = 0; i < 6; i++) {
208                                transparentPixel_16[i] = (byte)getByte(st + i);
209                            }
210                        } else {
211                            transparentPixel =
212                                      ((getShort(st + 0)&0xFF)<<16)
213                                    | ((getShort(st + 2)&0xFF)<< 8)
214                                    | ((getShort(st + 4)&0xFF)    );
215                        }
216                        break;
217                    case GRAY:  // doesn't deal with 16 bit colors properly
218                    case GRAY|ALPHA:  // doesn't deal with 16 bit colors properly
219                        pngassert(len==2);
220                        /* REMIND: Discarding the LSB for 16 bit depth here
221                         * means that the all pixels which match the MSB
222                         * will be treated as transparent.
223                         */
224                        int t = getShort(st);
225                        t = 0xFF & ((bitDepth == 16) ? (t >> 8) : t);
226                        transparentPixel = (t<<16) | (t<< 8) | t;
227                        break;
228                }
229                break;
230            case zTXtChunk: break;
231        }
232        return true;
233    }
234    @SuppressWarnings("serial") // JDK-implementation class
235    public class PNGException extends IOException {
236        PNGException(String s) { super(s); }
237    }
238  /* this is changed
239     public void run() {
240     */
241  public void produceImage() throws IOException, ImageFormatException {
242    /* this is not needed
243       ImageConsumer t = target;
244       if(t!=null) try {
245       */
246    try {
247            for(int i=0; i<signature.length; i++)
248              if((signature[i]&0xFF)!=underlyingInputStream.read())
249                throw new PNGException("Chunk signature mismatch");
250
251            InputStream is = new BufferedInputStream(new InflaterInputStream(inputStream,new Inflater()));
252
253            getData();
254
255            byte[] bPixels = null;
256            int[] wPixels = null;
257            int pixSize = width;
258            int rowStride;
259            int logDepth = 0;
260            switch(bitDepth) {
261                case  1: logDepth = 0; break;
262                case  2: logDepth = 1; break;
263                case  4: logDepth = 2; break;
264                case  8: logDepth = 3; break;
265                case 16: logDepth = 4; break;
266                default: throw new PNGException("invalid depth");
267            }
268            if(interlaceMethod!=0) {pixSize *= height;rowStride=width;}
269            else rowStride = 0;
270            int combinedType = colorType|(bitDepth<<3);
271            int bitMask = (1<<(bitDepth>=8?8:bitDepth))-1;
272            //Figure out the color model
273            switch(colorType) {
274                case COLOR|PALETTE:
275                case COLOR|PALETTE|ALPHA:
276                    if(red_map==null) throw new PNGException("palette expected");
277                    if(alpha_map==null)
278                        cm = new IndexColorModel(bitDepth,red_map.length,
279                            red_map,green_map,blue_map);
280                    else
281                        cm = new IndexColorModel(bitDepth,red_map.length,
282                            red_map,green_map,blue_map,alpha_map);
283                    bPixels = new byte[pixSize];
284                    break;
285                case GRAY:
286                    {   int llog = logDepth>=4 ? 3 : logDepth;
287                        if((cm=greyModels[llog]) == null) {
288                            int size = 1<<(1<<llog);
289
290                            byte ramp[] = new byte[size];
291                            for(int i = 0; i<size; i++) ramp[i] = (byte)(255*i/(size-1));
292
293                            if (transparentPixel == -1) {
294                                cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp);
295                            } else {
296                                cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp,
297                                                         (transparentPixel & 0xFF));
298                            }
299                            greyModels[llog] = cm;
300                        }
301                    }
302                    bPixels = new byte[pixSize];
303                    break;
304                case COLOR:
305                case COLOR|ALPHA:
306                case GRAY|ALPHA:
307                    cm = ColorModel.getRGBdefault();
308                    wPixels = new int[pixSize];
309                    break;
310                default:
311                    throw new PNGException("invalid color type");
312            }
313            /* this is going to be set in the pixel store
314              t.setColorModel(cm);
315            t.setHints(interlaceMethod !=0
316                       ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
317                       : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
318                         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
319                         */
320            // code added to make it work with ImageDecoder architecture
321            setDimensions(width, height);
322            setColorModel(cm);
323            int flags = (interlaceMethod !=0
324                       ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
325                       : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
326                         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
327            setHints(flags);
328            headerComplete();
329            // end of adding
330
331            int samplesPerPixel = ((colorType&PALETTE)!=0 ? 1
332                                 : ((colorType&COLOR)!=0 ? 3 : 1)+((colorType&ALPHA)!=0?1:0));
333            int bitsPerPixel = samplesPerPixel*bitDepth;
334            int bytesPerPixel = (bitsPerPixel+7)>>3;
335            int pass, passLimit;
336            if(interlaceMethod==0) { pass = -1; passLimit = 0; }
337            else { pass = 0; passLimit = 7; }
338            // These loops are far from being tuned.  They're this way to make them easy to
339            // debug.  Tuning comes later.
340            /* code changed. target not needed here
341               while(++pass<=passLimit && (t=target)!=null) {
342               */
343            while(++pass<=passLimit) {
344                int row = startingRow[pass];
345                int rowInc = rowIncrement[pass];
346                int colInc = colIncrement[pass];
347                int bWidth = blockWidth[pass];
348                int bHeight = blockHeight[pass];
349                int sCol = startingCol[pass];
350                int rowPixelWidth = (width-sCol+(colInc-1))/colInc;
351                int rowByteWidth = ((rowPixelWidth*bitsPerPixel)+7)>>3;
352                if(rowByteWidth==0) continue;
353                int pixelBufferInc = interlaceMethod==0 ? rowInc*width : 0;
354                int rowOffset = rowStride*row;
355                boolean firstRow = true;
356
357                byte[] rowByteBuffer = new byte[rowByteWidth];
358                byte[] prevRowByteBuffer = new byte[rowByteWidth];
359                /* code changed. target not needed here
360                   while (row < height && (t=target)!=null) {
361                   */
362                while (row < height) {
363                    int rowFilter = is.read();
364                    for (int rowFillPos=0;rowFillPos<rowByteWidth; ) {
365                        int n = is.read(rowByteBuffer,rowFillPos,rowByteWidth-rowFillPos);
366                        if(n<=0) throw new PNGException("missing data");
367                        rowFillPos+=n;
368                    }
369                    filterRow(rowByteBuffer,
370                              firstRow ? null : prevRowByteBuffer,
371                              rowFilter, rowByteWidth, bytesPerPixel);
372                    int col = sCol;
373                    int spos=0;
374                    int pixel = 0;
375                    while (col < width) {
376                        if(wPixels !=null) {
377                            switch(combinedType) {
378                                case COLOR|ALPHA|(8<<3):
379                                    wPixels[col+rowOffset] =
380                                          ((rowByteBuffer[spos  ]&0xFF)<<16)
381                                        | ((rowByteBuffer[spos+1]&0xFF)<< 8)
382                                        | ((rowByteBuffer[spos+2]&0xFF)    )
383                                        | ((rowByteBuffer[spos+3]&0xFF)<<24);
384                                    spos+=4;
385                                    break;
386                                case COLOR|ALPHA|(16<<3):
387                                    wPixels[col+rowOffset] =
388                                          ((rowByteBuffer[spos  ]&0xFF)<<16)
389                                        | ((rowByteBuffer[spos+2]&0xFF)<< 8)
390                                        | ((rowByteBuffer[spos+4]&0xFF)    )
391                                        | ((rowByteBuffer[spos+6]&0xFF)<<24);
392                                    spos+=8;
393                                    break;
394                                case COLOR|(8<<3):
395                                    pixel =
396                                          ((rowByteBuffer[spos  ]&0xFF)<<16)
397                                        | ((rowByteBuffer[spos+1]&0xFF)<< 8)
398                                        | ((rowByteBuffer[spos+2]&0xFF)    );
399                                    if (pixel != transparentPixel) {
400                                        pixel |= 0xff000000;
401                                    }
402                                    wPixels[col+rowOffset] = pixel;
403                                    spos+=3;
404                                    break;
405                                case COLOR|(16<<3):
406                                    pixel =
407                                              ((rowByteBuffer[spos  ]&0xFF)<<16)
408                                            | ((rowByteBuffer[spos+2]&0xFF)<< 8)
409                                            | ((rowByteBuffer[spos+4]&0xFF)    );
410
411                                    boolean isTransparent = (transparentPixel_16 != null);
412                                    for (int i = 0; isTransparent && (i < 6); i++) {
413                                        isTransparent &=
414                                                (rowByteBuffer[spos + i] & 0xFF) == (transparentPixel_16[i] & 0xFF);
415                                    }
416                                    if (!isTransparent)  {
417                                        pixel |= 0xff000000;
418                                    }
419                                    wPixels[col+rowOffset] = pixel;
420                                    spos+=6;
421                                    break;
422                                case GRAY|ALPHA|(8<<3):
423                                    { int tx = rowByteBuffer[spos]&0xFF;
424                                      wPixels[col+rowOffset] =
425                                          (tx<<16)|(tx<<8)|tx
426                                        |((rowByteBuffer[spos+1]&0xFF)<<24); }
427                                    spos+=2;
428                                    break;
429                                case GRAY|ALPHA|(16<<3):
430                                    { int tx = rowByteBuffer[spos]&0xFF;
431                                      wPixels[col+rowOffset] =
432                                          (tx<<16)|(tx<<8)|tx
433                                        |((rowByteBuffer[spos+2]&0xFF)<<24); }
434                                    spos+=4;
435                                    break;
436                                default: throw new PNGException("illegal type/depth");
437                            }
438                        } else switch(bitDepth) {
439                            case 1:
440                                bPixels[col+rowOffset] =
441                                    (byte)((rowByteBuffer[spos>>3]>>(7-(spos&7)))&1);
442                                spos++;
443                                break;
444                            case 2:
445                                bPixels[col+rowOffset] =
446                                    (byte)((rowByteBuffer[spos>>2]>>((3-(spos&3))*2))&3);
447                                spos++;
448                                break;
449                            case 4:
450                                bPixels[col+rowOffset] =
451                                    (byte)((rowByteBuffer[spos>>1]>>((1-(spos&1))*4))&15);
452                                spos++;
453                                break;
454                            case 8: bPixels[col+rowOffset] = rowByteBuffer[spos++];
455                                break;
456                            case 16: bPixels[col+rowOffset] = rowByteBuffer[spos]; spos+=2;
457                                break;
458                            default: throw new PNGException("illegal type/depth");
459                        }
460                        /*visit (row, col,
461                            min (bHeight, height - row),
462                            min (bWidth, width - col)); */
463                        col += colInc;
464                    }
465                    if(interlaceMethod==0)
466                      if(wPixels!=null) {
467                        /* code changed. target not needed here
468                          t.setPixels(0,row,width,1,cm,wPixels,0,width);
469                          */
470                       // code added to make it work with ImageDecoder arch
471                        sendPixels(0,row,width,1,wPixels,0,width);
472                        // end of adding
473                      }
474                      else {
475                        /* code changed. target not needed here
476                           t.setPixels(0,row,width,1,cm,bPixels,0,width);
477                           */
478                        // code added to make it work with ImageDecoder arch
479                        sendPixels(0,row,width,1,bPixels,0,width);
480                        //end of adding
481                      }
482                    row += rowInc;
483                    rowOffset += rowInc*rowStride;
484                    byte[] T = rowByteBuffer;
485                    rowByteBuffer = prevRowByteBuffer;
486                    prevRowByteBuffer = T;
487                    firstRow = false;
488                }
489                if(interlaceMethod!=0)
490                  if(wPixels!=null) {
491                    /* code changed. target not needed here
492                       t.setPixels(0,0,width,height,cm,wPixels,0,width);
493                       */
494                    // code added to make it work with ImageDecoder arch
495                      sendPixels(0,0,width,height,wPixels,0,width);
496                      //end of adding
497                  }
498                  else {
499                     /* code changed. target not needed here
500                        t.setPixels(0,0,width,height,cm,bPixels,0,width);
501                        */
502                    // code added to make it work with ImageDecoder arch
503                      sendPixels(0,0,width,height,bPixels,0,width);
504                      //end of adding
505                  }
506            }
507
508   /* Here, the function "visit(row,column,height,width)" obtains the
509      next transmitted pixel and paints a rectangle of the specified
510      height and width, whose upper-left corner is at the specified row
511      and column, using the color indicated by the pixel.  Note that row
512      and column are measured from 0,0 at the upper left corner. */
513
514            /* code not needed, don't deal with target
515             if((t=target)!=null) {
516               if(properties!=null) t.setProperties(properties);
517                 t.imageComplete(ImageConsumer.STATICIMAGEDONE);
518                 */
519
520              imageComplete(ImageConsumer.STATICIMAGEDONE, true);
521
522              /* code not needed }
523               is.close();
524               */
525        } catch(IOException e) {
526            if(!aborted) {
527                /* code not needed
528                   if((t=target)!=null) {
529                   PNGEncoder.prChunk(e.toString(),inbuf,pos,limit-pos,true);
530                */
531                property("error", e);
532                /* code not needed
533                   t.setProperties(properties);
534                   t.imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE);
535                */
536                imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE, true);
537                throw e;
538            }
539        } finally {
540          try { close(); } catch(Throwable e){}
541          /* code not needed
542             target = null;
543             endTurn();
544             */
545        }
546    }
547
548    private boolean sendPixels(int x, int y, int w, int h, int[] pixels,
549                               int offset, int pixlength) {
550        int count = setPixels(x, y, w, h, cm,
551                              pixels, offset, pixlength);
552        if (count <= 0) {
553            aborted = true;
554        }
555        return !aborted;
556    }
557    private boolean sendPixels(int x, int y, int w, int h, byte[] pixels,
558                               int offset, int pixlength) {
559        int count = setPixels(x, y, w, h, cm,
560                              pixels, offset, pixlength);
561        if (count <= 0) {
562            aborted = true;
563        }
564        return !aborted;
565    }
566
567    private void filterRow(byte rowByteBuffer[], byte[] prevRow,
568                           int rowFilter, int rowByteWidth, int bytesPerSample)
569        throws IOException {
570        int x = 0;
571        switch (rowFilter) {
572          case 0:
573            break;
574          case 1:
575            for (x = bytesPerSample; x < rowByteWidth; x++)
576                rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
577            break;
578          case 2:
579            if (prevRow != null)
580                for ( ; x < rowByteWidth; x++)
581                    rowByteBuffer[x] += prevRow[x];
582            break;
583          case 3:
584            if (prevRow != null) {
585                for ( ; x < bytesPerSample; x++)
586                    rowByteBuffer[x] += (0xff & prevRow[x])>>1;
587                for ( ; x < rowByteWidth; x++)
588                    rowByteBuffer[x] += ((prevRow[x]&0xFF) + (rowByteBuffer[x - bytesPerSample]&0xFF))>>1;
589            } else
590                for (x = bytesPerSample; x < rowByteWidth; x++)
591                    rowByteBuffer[x] += (rowByteBuffer[x - bytesPerSample]&0xFF)>>1;
592            break;
593          case 4:
594            if (prevRow != null) {
595                for ( ; x < bytesPerSample; x++)
596                    rowByteBuffer[x] += prevRow[x];
597                for ( ; x < rowByteWidth; x++) {
598                    int a, b, c, p, pa, pb, pc, rval;
599                    a = rowByteBuffer[x - bytesPerSample]&0xFF;
600                    b = prevRow[x]&0xFF;
601                    c = prevRow[x - bytesPerSample]&0xFF;
602                    p = a + b - c;
603                    pa = p > a ? p - a : a - p;
604                    pb = p > b ? p - b : b - p;
605                    pc = p > c ? p - c : c - p;
606                    rowByteBuffer[x] += (pa <= pb) && (pa <= pc) ? a : pb <= pc ? b : c;
607                }
608            } else
609                for (x = bytesPerSample; x < rowByteWidth; x++)
610                    rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
611            break;
612          default:
613            throw new PNGException("Illegal filter");
614        }
615    }
616    private static final byte[] startingRow =  { 0, 0, 0, 4, 0, 2, 0, 1 };
617    private static final byte[] startingCol =  { 0, 0, 4, 0, 2, 0, 1, 0 };
618    private static final byte[] rowIncrement = { 1, 8, 8, 8, 4, 4, 2, 2 };
619    private static final byte[] colIncrement = { 1, 8, 8, 4, 4, 2, 2, 1 };
620    private static final byte[] blockHeight =  { 1, 8, 8, 4, 4, 2, 2, 1 };
621    private static final byte[] blockWidth =   { 1, 8, 4, 4, 2, 2, 1, 1 };
622
623    //abstract public class ChunkReader extends FilterInputStream {
624  int pos, limit;
625    int chunkStart;
626   int chunkKey, chunkLength, chunkCRC;
627    boolean seenEOF;
628
629    private static final byte[] signature = { (byte) 137, (byte) 80, (byte) 78,
630        (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
631
632  PNGFilterInputStream inputStream;
633  InputStream underlyingInputStream;
634
635  /* code changed
636    public PNGImageDecoder(InputStream in, ImageConsumer t) throws IOException {
637    */
638  public PNGImageDecoder(InputStreamImageSource src, InputStream input) throws IOException {
639    // code added
640    super(src, input);
641    inputStream = new PNGFilterInputStream(this, input);
642    underlyingInputStream = inputStream.underlyingInputStream;
643    // end of adding
644    /* code changed
645       super(in);
646       target = t;
647       waitTurn();
648       new Thread(this).start();
649       */
650    }
651  /* code changed to make it work with ImageDecoder architecture
652    static int ThreadLimit = 10;
653    private static synchronized void waitTurn() {
654        try {
655            while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000);
656        } catch(InterruptedException e){}
657        ThreadLimit--;
658    }
659    private static synchronized void endTurn() {
660        if(ThreadLimit<=0) PNGImageDecoder.class.notify();
661        ThreadLimit++;
662    }
663    */
664    byte[] inbuf = new byte[4096];
665    private void fill() throws IOException {
666        if(!seenEOF) {
667            if(pos>0 && pos<limit) {
668                System.arraycopy(inbuf,pos,inbuf,0,limit-pos);
669                limit = limit-pos;
670                pos = 0;
671            } else if(pos>=limit) {
672                pos = 0; limit = 0;
673            }
674            int bsize = inbuf.length;
675            while(limit<bsize) {
676                int n = underlyingInputStream.read(inbuf,limit,bsize-limit);
677                if(n<=0) { seenEOF=true; break; }
678                limit += n;
679            }
680        }
681    }
682    private boolean need(int n) throws IOException {
683        if(limit-pos>=n) return true;
684        fill();
685        if(limit-pos>=n) return true;
686        if(seenEOF) return false;
687        byte nin[] = new byte[n+100];
688        System.arraycopy(inbuf,pos,nin,0,limit-pos);
689        limit = limit-pos;
690        pos = 0;
691        inbuf = nin;
692        fill();
693        return limit-pos>=n;
694    }
695    private final int getInt(int pos) {
696        return ((inbuf[pos  ]&0xFF)<<24)
697             | ((inbuf[pos+1]&0xFF)<<16)
698             | ((inbuf[pos+2]&0xFF)<< 8)
699             | ((inbuf[pos+3]&0xFF)    );
700    }
701    private final int getShort(int pos) {
702        return (short)(((inbuf[pos  ]&0xFF)<<8)
703                     | ((inbuf[pos+1]&0xFF)   ));
704    }
705    private final int getByte(int pos) {
706        return inbuf[pos]&0xFF;
707    }
708    private final boolean getChunk() throws IOException {
709        chunkLength = 0;
710        if (!need(8)) return false;
711        chunkLength = getInt(pos);
712        chunkKey = getInt(pos+4);
713        if(chunkLength<0) throw new PNGException("bogus length: "+chunkLength);
714        if (!need(chunkLength+12)) return false;
715        chunkCRC = getInt(pos+8+chunkLength);
716        chunkStart = pos+8;
717        int calcCRC = crc(inbuf,pos+4,chunkLength+4);
718        if(chunkCRC!=calcCRC && checkCRC) throw new PNGException("crc corruption");
719        pos+=chunkLength+12;
720        return true;
721    }
722    private void readAll() throws IOException {
723        while(getChunk()) handleChunk(chunkKey,inbuf,chunkStart,chunkLength);
724    }
725    boolean getData() throws IOException {
726        while(chunkLength==0 && getChunk())
727            if(handleChunk(chunkKey,inbuf,chunkStart,chunkLength))
728                chunkLength = 0;
729        return chunkLength>0;
730    }
731    //abstract protected boolean handleChunk(int key, byte[] buf, int st, int len)
732    //    throws IOException;
733    private static boolean checkCRC = true;
734    public static boolean getCheckCRC() { return checkCRC; }
735    public static void setCheckCRC(boolean c) { checkCRC = c; }
736
737    protected void wrc(int c) {
738        c = c&0xFF;
739        if(c<=' '||c>'z') c = '?';
740        System.out.write(c);
741    }
742    protected void wrk(int n) {
743        wrc(n>>24);
744        wrc(n>>16);
745        wrc(n>>8);
746        wrc(n);
747    }
748    public void print() {
749        wrk(chunkKey);
750        System.out.print(" "+chunkLength+"\n");
751    }
752
753    /* Table of CRCs of all 8-bit messages. */
754    private static final int[] crc_table = new int[256];
755
756    /* Make the table for a fast CRC. */
757    static {
758        for (int n = 0; n < 256; n++) {
759            int c = n;
760            for (int k = 0; k < 8; k++)
761                if ((c & 1) != 0)
762                    c = 0xedb88320 ^ (c >>> 1);
763                else
764                    c = c >>> 1;
765            crc_table[n] = c;
766        }
767    }
768
769    /* Update a running CRC with the bytes buf[0..len-1]--the CRC
770    should be initialized to all 1's, and the transmitted value
771    is the 1's complement of the final running CRC (see the
772    crc() routine below)). */
773
774    private static int update_crc(int crc, byte[] buf, int offset, int len) {
775        int c = crc;
776        while (--len>=0)
777            c = crc_table[(c ^ buf[offset++]) & 0xff] ^ (c >>> 8);
778        return c;
779    }
780
781    /* Return the CRC of the bytes buf[0..len-1]. */
782    private static int crc(byte[] buf, int offset, int len) {
783        return update_crc(0xffffffff, buf, offset, len) ^ 0xffffffff;
784    }
785    public static class Chromaticities {
786        public float whiteX, whiteY, redX, redY, greenX, greenY, blueX, blueY;
787        Chromaticities(int wx, int wy, int rx, int ry, int gx, int gy, int bx, int by) {
788            whiteX = wx/100000.0f;
789            whiteY = wy/100000.0f;
790            redX = rx/100000.0f;
791            redY = ry/100000.0f;
792            greenX = gx/100000.0f;
793            greenY = gy/100000.0f;
794            blueX = bx/100000.0f;
795            blueY = by/100000.0f;
796        }
797        public String toString() {
798            return "Chromaticities(white="+whiteX+","+whiteY+";red="+
799                redX+","+redY+";green="+
800                greenX+","+greenY+";blue="+
801                blueX+","+blueY+")";
802        }
803    }
804}
805
806// the following class are added to make it work with ImageDecoder architecture
807
808class PNGFilterInputStream extends FilterInputStream {
809  PNGImageDecoder owner;
810  public InputStream underlyingInputStream;
811  public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) {
812    super(is);
813    underlyingInputStream = in;
814    this.owner = owner;
815  }
816
817    public int available() throws IOException {
818        return owner.limit-owner.pos+in.available();}
819    public boolean markSupported() { return false; }
820    public int read() throws IOException {
821        if(owner.chunkLength<=0) if(!owner.getData()) return -1;
822        owner.chunkLength--;
823        return owner.inbuf[owner.chunkStart++]&0xFF;
824    }
825    public int read(byte[] b) throws IOException{return read(b,0,b.length);}
826    public int read(byte[] b, int st, int len) throws IOException {
827        if(owner.chunkLength<=0) if(!owner.getData()) return -1;
828        if(owner.chunkLength<len) len = owner.chunkLength;
829        System.arraycopy(owner.inbuf,owner.chunkStart,b,st,len);
830        owner.chunkLength-=len;
831        owner.chunkStart+=len;
832        return len;
833    }
834  public long skip(long n) throws IOException {
835        int i;
836        for(i = 0; i<n && read()>=0; i++);
837        return i;
838    }
839
840
841}
842