1 /*
   2  * Copyright (c) 1999, 2006, 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=ftp://ds.internic.net/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     public class PNGException extends IOException {
 235         PNGException(String s) { super(s); }
 236     }
 237   /* this is changed
 238      public void run() {
 239      */
 240   public void produceImage() throws IOException, ImageFormatException {
 241     /* this is not needed
 242        ImageConsumer t = target;
 243        if(t!=null) try {
 244        */
 245     try {
 246             for(int i=0; i<signature.length; i++)
 247               if((signature[i]&0xFF)!=underlyingInputStream.read())
 248                 throw new PNGException("Chunk signature mismatch");
 249 
 250             InputStream is = new BufferedInputStream(new InflaterInputStream(inputStream,new Inflater()));
 251 
 252             getData();
 253 
 254             byte[] bPixels = null;
 255             int[] wPixels = null;
 256             int pixSize = width;
 257             int rowStride;
 258             int logDepth = 0;
 259             switch(bitDepth) {
 260                 case  1: logDepth = 0; break;
 261                 case  2: logDepth = 1; break;
 262                 case  4: logDepth = 2; break;
 263                 case  8: logDepth = 3; break;
 264                 case 16: logDepth = 4; break;
 265                 default: throw new PNGException("invalid depth");
 266             }
 267             if(interlaceMethod!=0) {pixSize *= height;rowStride=width;}
 268             else rowStride = 0;
 269             int combinedType = colorType|(bitDepth<<3);
 270             int bitMask = (1<<(bitDepth>=8?8:bitDepth))-1;
 271             //Figure out the color model
 272             switch(colorType) {
 273                 case COLOR|PALETTE:
 274                 case COLOR|PALETTE|ALPHA:
 275                     if(red_map==null) throw new PNGException("palette expected");
 276                     if(alpha_map==null)
 277                         cm = new IndexColorModel(bitDepth,red_map.length,
 278                             red_map,green_map,blue_map);
 279                     else
 280                         cm = new IndexColorModel(bitDepth,red_map.length,
 281                             red_map,green_map,blue_map,alpha_map);
 282                     bPixels = new byte[pixSize];
 283                     break;
 284                 case GRAY:
 285                     {   int llog = logDepth>=4 ? 3 : logDepth;
 286                         if((cm=greyModels[llog]) == null) {
 287                             int size = 1<<(1<<llog);
 288 
 289                             byte ramp[] = new byte[size];
 290                             for(int i = 0; i<size; i++) ramp[i] = (byte)(255*i/(size-1));
 291 
 292                             if (transparentPixel == -1) {
 293                                 cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp);
 294                             } else {
 295                                 cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp,
 296                                                          (transparentPixel & 0xFF));
 297                             }
 298                             greyModels[llog] = cm;
 299                         }
 300                     }
 301                     bPixels = new byte[pixSize];
 302                     break;
 303                 case COLOR:
 304                 case COLOR|ALPHA:
 305                 case GRAY|ALPHA:
 306                     cm = ColorModel.getRGBdefault();
 307                     wPixels = new int[pixSize];
 308                     break;
 309                 default:
 310                     throw new PNGException("invalid color type");
 311             }
 312             /* this is going to be set in the pixel store
 313               t.setColorModel(cm);
 314             t.setHints(interlaceMethod !=0
 315                        ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
 316                        : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
 317                          ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
 318                          */
 319             // code added to make it work with ImageDecoder architecture
 320             setDimensions(width, height);
 321             setColorModel(cm);
 322             int flags = (interlaceMethod !=0
 323                        ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
 324                        : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
 325                          ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
 326             setHints(flags);
 327             headerComplete();
 328             // end of adding
 329 
 330             int samplesPerPixel = ((colorType&PALETTE)!=0 ? 1
 331                                  : ((colorType&COLOR)!=0 ? 3 : 1)+((colorType&ALPHA)!=0?1:0));
 332             int bitsPerPixel = samplesPerPixel*bitDepth;
 333             int bytesPerPixel = (bitsPerPixel+7)>>3;
 334             int pass, passLimit;
 335             if(interlaceMethod==0) { pass = -1; passLimit = 0; }
 336             else { pass = 0; passLimit = 7; }
 337             // These loops are far from being tuned.  They're this way to make them easy to
 338             // debug.  Tuning comes later.
 339             /* code changed. target not needed here
 340                while(++pass<=passLimit && (t=target)!=null) {
 341                */
 342             while(++pass<=passLimit) {
 343                 int row = startingRow[pass];
 344                 int rowInc = rowIncrement[pass];
 345                 int colInc = colIncrement[pass];
 346                 int bWidth = blockWidth[pass];
 347                 int bHeight = blockHeight[pass];
 348                 int sCol = startingCol[pass];
 349                 int rowPixelWidth = (width-sCol+(colInc-1))/colInc;
 350                 int rowByteWidth = ((rowPixelWidth*bitsPerPixel)+7)>>3;
 351                 if(rowByteWidth==0) continue;
 352                 int pixelBufferInc = interlaceMethod==0 ? rowInc*width : 0;
 353                 int rowOffset = rowStride*row;
 354                 boolean firstRow = true;
 355 
 356                 byte[] rowByteBuffer = new byte[rowByteWidth];
 357                 byte[] prevRowByteBuffer = new byte[rowByteWidth];
 358                 /* code changed. target not needed here
 359                    while (row < height && (t=target)!=null) {
 360                    */
 361                 while (row < height) {
 362                     int rowFilter = is.read();
 363                     for (int rowFillPos=0;rowFillPos<rowByteWidth; ) {
 364                         int n = is.read(rowByteBuffer,rowFillPos,rowByteWidth-rowFillPos);
 365                         if(n<=0) throw new PNGException("missing data");
 366                         rowFillPos+=n;
 367                     }
 368                     filterRow(rowByteBuffer,
 369                               firstRow ? null : prevRowByteBuffer,
 370                               rowFilter, rowByteWidth, bytesPerPixel);
 371                     int col = sCol;
 372                     int spos=0;
 373                     int pixel = 0;
 374                     while (col < width) {
 375                         if(wPixels !=null) {
 376                             switch(combinedType) {
 377                                 case COLOR|ALPHA|(8<<3):
 378                                     wPixels[col+rowOffset] =
 379                                           ((rowByteBuffer[spos  ]&0xFF)<<16)
 380                                         | ((rowByteBuffer[spos+1]&0xFF)<< 8)
 381                                         | ((rowByteBuffer[spos+2]&0xFF)    )
 382                                         | ((rowByteBuffer[spos+3]&0xFF)<<24);
 383                                     spos+=4;
 384                                     break;
 385                                 case COLOR|ALPHA|(16<<3):
 386                                     wPixels[col+rowOffset] =
 387                                           ((rowByteBuffer[spos  ]&0xFF)<<16)
 388                                         | ((rowByteBuffer[spos+2]&0xFF)<< 8)
 389                                         | ((rowByteBuffer[spos+4]&0xFF)    )
 390                                         | ((rowByteBuffer[spos+6]&0xFF)<<24);
 391                                     spos+=8;
 392                                     break;
 393                                 case COLOR|(8<<3):
 394                                     pixel =
 395                                           ((rowByteBuffer[spos  ]&0xFF)<<16)
 396                                         | ((rowByteBuffer[spos+1]&0xFF)<< 8)
 397                                         | ((rowByteBuffer[spos+2]&0xFF)    );
 398                                     if (pixel != transparentPixel) {
 399                                         pixel |= 0xff000000;
 400                                     }
 401                                     wPixels[col+rowOffset] = pixel;
 402                                     spos+=3;
 403                                     break;
 404                                 case COLOR|(16<<3):
 405                                     pixel =
 406                                               ((rowByteBuffer[spos  ]&0xFF)<<16)
 407                                             | ((rowByteBuffer[spos+2]&0xFF)<< 8)
 408                                             | ((rowByteBuffer[spos+4]&0xFF)    );
 409 
 410                                     boolean isTransparent = (transparentPixel_16 != null);
 411                                     for (int i = 0; isTransparent && (i < 6); i++) {
 412                                         isTransparent &=
 413                                                 (rowByteBuffer[spos + i] & 0xFF) == (transparentPixel_16[i] & 0xFF);
 414                                     }
 415                                     if (!isTransparent)  {
 416                                         pixel |= 0xff000000;
 417                                     }
 418                                     wPixels[col+rowOffset] = pixel;
 419                                     spos+=6;
 420                                     break;
 421                                 case GRAY|ALPHA|(8<<3):
 422                                     { int tx = rowByteBuffer[spos]&0xFF;
 423                                       wPixels[col+rowOffset] =
 424                                           (tx<<16)|(tx<<8)|tx
 425                                         |((rowByteBuffer[spos+1]&0xFF)<<24); }
 426                                     spos+=2;
 427                                     break;
 428                                 case GRAY|ALPHA|(16<<3):
 429                                     { int tx = rowByteBuffer[spos]&0xFF;
 430                                       wPixels[col+rowOffset] =
 431                                           (tx<<16)|(tx<<8)|tx
 432                                         |((rowByteBuffer[spos+2]&0xFF)<<24); }
 433                                     spos+=4;
 434                                     break;
 435                                 default: throw new PNGException("illegal type/depth");
 436                             }
 437                         } else switch(bitDepth) {
 438                             case 1:
 439                                 bPixels[col+rowOffset] =
 440                                     (byte)((rowByteBuffer[spos>>3]>>(7-(spos&7)))&1);
 441                                 spos++;
 442                                 break;
 443                             case 2:
 444                                 bPixels[col+rowOffset] =
 445                                     (byte)((rowByteBuffer[spos>>2]>>((3-(spos&3))*2))&3);
 446                                 spos++;
 447                                 break;
 448                             case 4:
 449                                 bPixels[col+rowOffset] =
 450                                     (byte)((rowByteBuffer[spos>>1]>>((1-(spos&1))*4))&15);
 451                                 spos++;
 452                                 break;
 453                             case 8: bPixels[col+rowOffset] = rowByteBuffer[spos++];
 454                                 break;
 455                             case 16: bPixels[col+rowOffset] = rowByteBuffer[spos]; spos+=2;
 456                                 break;
 457                             default: throw new PNGException("illegal type/depth");
 458                         }
 459                         /*visit (row, col,
 460                             min (bHeight, height - row),
 461                             min (bWidth, width - col)); */
 462                         col += colInc;
 463                     }
 464                     if(interlaceMethod==0)
 465                       if(wPixels!=null) {
 466                         /* code changed. target not needed here
 467                           t.setPixels(0,row,width,1,cm,wPixels,0,width);
 468                           */
 469                        // code added to make it work with ImageDecoder arch
 470                         sendPixels(0,row,width,1,wPixels,0,width);
 471                         // end of adding
 472                       }
 473                       else {
 474                         /* code changed. target not needed here
 475                            t.setPixels(0,row,width,1,cm,bPixels,0,width);
 476                            */
 477                         // code added to make it work with ImageDecoder arch
 478                         sendPixels(0,row,width,1,bPixels,0,width);
 479                         //end of adding
 480                       }
 481                     row += rowInc;
 482                     rowOffset += rowInc*rowStride;
 483                     byte[] T = rowByteBuffer;
 484                     rowByteBuffer = prevRowByteBuffer;
 485                     prevRowByteBuffer = T;
 486                     firstRow = false;
 487                 }
 488                 if(interlaceMethod!=0)
 489                   if(wPixels!=null) {
 490                     /* code changed. target not needed here
 491                        t.setPixels(0,0,width,height,cm,wPixels,0,width);
 492                        */
 493                     // code added to make it work with ImageDecoder arch
 494                       sendPixels(0,0,width,height,wPixels,0,width);
 495                       //end of adding
 496                   }
 497                   else {
 498                      /* code changed. target not needed here
 499                         t.setPixels(0,0,width,height,cm,bPixels,0,width);
 500                         */
 501                     // code added to make it work with ImageDecoder arch
 502                       sendPixels(0,0,width,height,bPixels,0,width);
 503                       //end of adding
 504                   }
 505             }
 506 
 507    /* Here, the function "visit(row,column,height,width)" obtains the
 508       next transmitted pixel and paints a rectangle of the specified
 509       height and width, whose upper-left corner is at the specified row
 510       and column, using the color indicated by the pixel.  Note that row
 511       and column are measured from 0,0 at the upper left corner. */
 512 
 513             /* code not needed, don't deal with target
 514              if((t=target)!=null) {
 515                if(properties!=null) t.setProperties(properties);
 516                  t.imageComplete(ImageConsumer.STATICIMAGEDONE);
 517                  */
 518 
 519               imageComplete(ImageConsumer.STATICIMAGEDONE, true);
 520 
 521               /* code not needed }
 522                is.close();
 523                */
 524         } catch(IOException e) {
 525             if(!aborted) {
 526                 /* code not needed
 527                    if((t=target)!=null) {
 528                    PNGEncoder.prChunk(e.toString(),inbuf,pos,limit-pos,true);
 529                 */
 530                 property("error", e);
 531                 /* code not needed
 532                    t.setProperties(properties);
 533                    t.imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE);
 534                 */
 535                 imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE, true);
 536                 throw e;
 537             }
 538         } finally {
 539           try { close(); } catch(Throwable e){}
 540           /* code not needed
 541              target = null;
 542              endTurn();
 543              */
 544         }
 545     }
 546 
 547     private boolean sendPixels(int x, int y, int w, int h, int[] pixels,
 548                                int offset, int pixlength) {
 549         int count = setPixels(x, y, w, h, cm,
 550                               pixels, offset, pixlength);
 551         if (count <= 0) {
 552             aborted = true;
 553         }
 554         return !aborted;
 555     }
 556     private boolean sendPixels(int x, int y, int w, int h, byte[] pixels,
 557                                int offset, int pixlength) {
 558         int count = setPixels(x, y, w, h, cm,
 559                               pixels, offset, pixlength);
 560         if (count <= 0) {
 561             aborted = true;
 562         }
 563         return !aborted;
 564     }
 565 
 566     private void filterRow(byte rowByteBuffer[], byte[] prevRow,
 567                            int rowFilter, int rowByteWidth, int bytesPerSample)
 568         throws IOException {
 569         int x = 0;
 570         switch (rowFilter) {
 571           case 0:
 572             break;
 573           case 1:
 574             for (x = bytesPerSample; x < rowByteWidth; x++)
 575                 rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
 576             break;
 577           case 2:
 578             if (prevRow != null)
 579                 for ( ; x < rowByteWidth; x++)
 580                     rowByteBuffer[x] += prevRow[x];
 581             break;
 582           case 3:
 583             if (prevRow != null) {
 584                 for ( ; x < bytesPerSample; x++)
 585                     rowByteBuffer[x] += (0xff & prevRow[x])>>1;
 586                 for ( ; x < rowByteWidth; x++)
 587                     rowByteBuffer[x] += ((prevRow[x]&0xFF) + (rowByteBuffer[x - bytesPerSample]&0xFF))>>1;
 588             } else
 589                 for (x = bytesPerSample; x < rowByteWidth; x++)
 590                     rowByteBuffer[x] += (rowByteBuffer[x - bytesPerSample]&0xFF)>>1;
 591             break;
 592           case 4:
 593             if (prevRow != null) {
 594                 for ( ; x < bytesPerSample; x++)
 595                     rowByteBuffer[x] += prevRow[x];
 596                 for ( ; x < rowByteWidth; x++) {
 597                     int a, b, c, p, pa, pb, pc, rval;
 598                     a = rowByteBuffer[x - bytesPerSample]&0xFF;
 599                     b = prevRow[x]&0xFF;
 600                     c = prevRow[x - bytesPerSample]&0xFF;
 601                     p = a + b - c;
 602                     pa = p > a ? p - a : a - p;
 603                     pb = p > b ? p - b : b - p;
 604                     pc = p > c ? p - c : c - p;
 605                     rowByteBuffer[x] += (pa <= pb) && (pa <= pc) ? a : pb <= pc ? b : c;
 606                 }
 607             } else
 608                 for (x = bytesPerSample; x < rowByteWidth; x++)
 609                     rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
 610             break;
 611           default:
 612             throw new PNGException("Illegal filter");
 613         }
 614     }
 615     private static final byte[] startingRow =  { 0, 0, 0, 4, 0, 2, 0, 1 };
 616     private static final byte[] startingCol =  { 0, 0, 4, 0, 2, 0, 1, 0 };
 617     private static final byte[] rowIncrement = { 1, 8, 8, 8, 4, 4, 2, 2 };
 618     private static final byte[] colIncrement = { 1, 8, 8, 4, 4, 2, 2, 1 };
 619     private static final byte[] blockHeight =  { 1, 8, 8, 4, 4, 2, 2, 1 };
 620     private static final byte[] blockWidth =   { 1, 8, 4, 4, 2, 2, 1, 1 };
 621 
 622     //abstract public class ChunkReader extends FilterInputStream {
 623   int pos, limit;
 624     int chunkStart;
 625    int chunkKey, chunkLength, chunkCRC;
 626     boolean seenEOF;
 627 
 628     private static final byte[] signature = { (byte) 137, (byte) 80, (byte) 78,
 629         (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
 630 
 631   PNGFilterInputStream inputStream;
 632   InputStream underlyingInputStream;
 633 
 634   /* code changed
 635     public PNGImageDecoder(InputStream in, ImageConsumer t) throws IOException {
 636     */
 637   public PNGImageDecoder(InputStreamImageSource src, InputStream input) throws IOException {
 638     // code added
 639     super(src, input);
 640     inputStream = new PNGFilterInputStream(this, input);
 641     underlyingInputStream = inputStream.underlyingInputStream;
 642     // end of adding
 643     /* code changed
 644        super(in);
 645        target = t;
 646        waitTurn();
 647        new Thread(this).start();
 648        */
 649     }
 650   /* code changed to make it work with ImageDecoder architecture
 651     static int ThreadLimit = 10;
 652     private synchronized static void waitTurn() {
 653         try {
 654             while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000);
 655         } catch(InterruptedException e){}
 656         ThreadLimit--;
 657     }
 658     private synchronized static void endTurn() {
 659         if(ThreadLimit<=0) PNGImageDecoder.class.notify();
 660         ThreadLimit++;
 661     }
 662     */
 663     byte[] inbuf = new byte[4096];
 664     private void fill() throws IOException {
 665         if(!seenEOF) {
 666             if(pos>0 && pos<limit) {
 667                 System.arraycopy(inbuf,pos,inbuf,0,limit-pos);
 668                 limit = limit-pos;
 669                 pos = 0;
 670             } else if(pos>=limit) {
 671                 pos = 0; limit = 0;
 672             }
 673             int bsize = inbuf.length;
 674             while(limit<bsize) {
 675                 int n = underlyingInputStream.read(inbuf,limit,bsize-limit);
 676                 if(n<=0) { seenEOF=true; break; }
 677                 limit += n;
 678             }
 679         }
 680     }
 681     private boolean need(int n) throws IOException {
 682         if(limit-pos>=n) return true;
 683         fill();
 684         if(limit-pos>=n) return true;
 685         if(seenEOF) return false;
 686         byte nin[] = new byte[n+100];
 687         System.arraycopy(inbuf,pos,nin,0,limit-pos);
 688         limit = limit-pos;
 689         pos = 0;
 690         inbuf = nin;
 691         fill();
 692         return limit-pos>=n;
 693     }
 694     private final int getInt(int pos) {
 695         return ((inbuf[pos  ]&0xFF)<<24)
 696              | ((inbuf[pos+1]&0xFF)<<16)
 697              | ((inbuf[pos+2]&0xFF)<< 8)
 698              | ((inbuf[pos+3]&0xFF)    );
 699     }
 700     private final int getShort(int pos) {
 701         return (short)(((inbuf[pos  ]&0xFF)<<8)
 702                      | ((inbuf[pos+1]&0xFF)   ));
 703     }
 704     private final int getByte(int pos) {
 705         return inbuf[pos]&0xFF;
 706     }
 707     private final boolean getChunk() throws IOException {
 708         chunkLength = 0;
 709         if (!need(8)) return false;
 710         chunkLength = getInt(pos);
 711         chunkKey = getInt(pos+4);
 712         if(chunkLength<0) throw new PNGException("bogus length: "+chunkLength);
 713         if (!need(chunkLength+12)) return false;
 714         chunkCRC = getInt(pos+8+chunkLength);
 715         chunkStart = pos+8;
 716         int calcCRC = crc(inbuf,pos+4,chunkLength+4);
 717         if(chunkCRC!=calcCRC && checkCRC) throw new PNGException("crc corruption");
 718         pos+=chunkLength+12;
 719         return true;
 720     }
 721     private void readAll() throws IOException {
 722         while(getChunk()) handleChunk(chunkKey,inbuf,chunkStart,chunkLength);
 723     }
 724     boolean getData() throws IOException {
 725         while(chunkLength==0 && getChunk())
 726             if(handleChunk(chunkKey,inbuf,chunkStart,chunkLength))
 727                 chunkLength = 0;
 728         return chunkLength>0;
 729     }
 730     //abstract protected boolean handleChunk(int key, byte[] buf, int st, int len)
 731     //    throws IOException;
 732     private static boolean checkCRC = true;
 733     public static boolean getCheckCRC() { return checkCRC; }
 734     public static void setCheckCRC(boolean c) { checkCRC = c; }
 735 
 736     protected void wrc(int c) {
 737         c = c&0xFF;
 738         if(c<=' '||c>'z') c = '?';
 739         System.out.write(c);
 740     }
 741     protected void wrk(int n) {
 742         wrc(n>>24);
 743         wrc(n>>16);
 744         wrc(n>>8);
 745         wrc(n);
 746     }
 747     public void print() {
 748         wrk(chunkKey);
 749         System.out.print(" "+chunkLength+"\n");
 750     }
 751 
 752     /* Table of CRCs of all 8-bit messages. */
 753     private static final int[] crc_table = new int[256];
 754 
 755     /* Make the table for a fast CRC. */
 756     static {
 757         for (int n = 0; n < 256; n++) {
 758             int c = n;
 759             for (int k = 0; k < 8; k++)
 760                 if ((c & 1) != 0)
 761                     c = 0xedb88320 ^ (c >>> 1);
 762                 else
 763                     c = c >>> 1;
 764             crc_table[n] = c;
 765         }
 766     }
 767 
 768     /* Update a running CRC with the bytes buf[0..len-1]--the CRC
 769     should be initialized to all 1's, and the transmitted value
 770     is the 1's complement of the final running CRC (see the
 771     crc() routine below)). */
 772 
 773     static private int update_crc(int crc, byte[] buf, int offset, int len) {
 774         int c = crc;
 775         while (--len>=0)
 776             c = crc_table[(c ^ buf[offset++]) & 0xff] ^ (c >>> 8);
 777         return c;
 778     }
 779 
 780     /* Return the CRC of the bytes buf[0..len-1]. */
 781     static private int crc(byte[] buf, int offset, int len) {
 782         return update_crc(0xffffffff, buf, offset, len) ^ 0xffffffff;
 783     }
 784     public static class Chromaticities {
 785         public float whiteX, whiteY, redX, redY, greenX, greenY, blueX, blueY;
 786         Chromaticities(int wx, int wy, int rx, int ry, int gx, int gy, int bx, int by) {
 787             whiteX = wx/100000.0f;
 788             whiteY = wy/100000.0f;
 789             redX = rx/100000.0f;
 790             redY = ry/100000.0f;
 791             greenX = gx/100000.0f;
 792             greenY = gy/100000.0f;
 793             blueX = bx/100000.0f;
 794             blueY = by/100000.0f;
 795         }
 796         public String toString() {
 797             return "Chromaticities(white="+whiteX+","+whiteY+";red="+
 798                 redX+","+redY+";green="+
 799                 greenX+","+greenY+";blue="+
 800                 blueX+","+blueY+")";
 801         }
 802     }
 803 }
 804 
 805 // the following class are added to make it work with ImageDecoder architecture
 806 
 807 class PNGFilterInputStream extends FilterInputStream {
 808   PNGImageDecoder owner;
 809   public InputStream underlyingInputStream;
 810   public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) {
 811     super(is);
 812     underlyingInputStream = in;
 813     this.owner = owner;
 814   }
 815 
 816     public int available() throws IOException {
 817         return owner.limit-owner.pos+in.available();}
 818     public boolean markSupported() { return false; }
 819     public int read() throws IOException {
 820         if(owner.chunkLength<=0) if(!owner.getData()) return -1;
 821         owner.chunkLength--;
 822         return owner.inbuf[owner.chunkStart++]&0xFF;
 823     }
 824     public int read(byte[] b) throws IOException{return read(b,0,b.length);}
 825     public int read(byte[] b, int st, int len) throws IOException {
 826         if(owner.chunkLength<=0) if(!owner.getData()) return -1;
 827         if(owner.chunkLength<len) len = owner.chunkLength;
 828         System.arraycopy(owner.inbuf,owner.chunkStart,b,st,len);
 829         owner.chunkLength-=len;
 830         owner.chunkStart+=len;
 831         return len;
 832     }
 833   public long skip(long n) throws IOException {
 834         int i;
 835         for(i = 0; i<n && read()>=0; i++);
 836         return i;
 837     }
 838 
 839 
 840 }