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 
  26 package sun.awt.image;
  27 
  28 import java.io.*;
  29 import java.util.*;
  30 import java.util.zip.*;
  31 import java.awt.image.*;
  32 import 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
  38 public class PNGImageDecoder extends FilterInputStream implements Runnable
  39 { */
  40 
  41 public 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 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,new Float(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 synchronized static void waitTurn() {
 654         try {
 655             while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000);
 656         } catch(InterruptedException e){}
 657         ThreadLimit--;
 658     }
 659     private synchronized static 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     static private 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     static private 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 
 808 class 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 }