1 /* 2 * Copyright (c) 1999, 2018, 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<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 property(String key,Object value)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 } property(String key,float value)89 private void property(String key,float value) { 90 property(key, Float.valueOf(value)); 91 } pngassert(boolean b)92 private void pngassert(boolean b) throws IOException { 93 if(!b) { 94 PNGException e = new PNGException("Broken file"); 95 e.printStackTrace(); 96 throw e; 97 } 98 } handleChunk(int key, byte[] buf, int st, int len)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 { PNGException(String s)236 PNGException(String s) { super(s); } 237 } 238 /* this is changed 239 public void run() { 240 */ produceImage()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 sendPixels(int x, int y, int w, int h, int[] pixels, int offset, int pixlength)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 } sendPixels(int x, int y, int w, int h, byte[] pixels, int offset, int pixlength)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 filterRow(byte[] rowByteBuffer, byte[] prevRow, int rowFilter, int rowByteWidth, int bytesPerSample)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 */ PNGImageDecoder(InputStreamImageSource src, InputStream input)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]; fill()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 } need(int n)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 } getInt(int pos)695 private 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 } getShort(int pos)701 private int getShort(int pos) { 702 return (short)(((inbuf[pos ]&0xFF)<<8) 703 | ((inbuf[pos+1]&0xFF) )); 704 } getByte(int pos)705 private int getByte(int pos) { 706 return inbuf[pos]&0xFF; 707 } getChunk()708 private 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 } readAll()722 private void readAll() throws IOException { 723 while(getChunk()) handleChunk(chunkKey,inbuf,chunkStart,chunkLength); 724 } getData()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; getCheckCRC()734 public static boolean getCheckCRC() { return checkCRC; } setCheckCRC(boolean c)735 public static void setCheckCRC(boolean c) { checkCRC = c; } 736 wrc(int c)737 protected void wrc(int c) { 738 c = c&0xFF; 739 if(c<=' '||c>'z') c = '?'; 740 System.out.write(c); 741 } wrk(int n)742 protected void wrk(int n) { 743 wrc(n>>24); 744 wrc(n>>16); 745 wrc(n>>8); 746 wrc(n); 747 } print()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 update_crc(int crc, byte[] buf, int offset, int len)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]. */ crc(byte[] buf, int offset, int len)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; Chromaticities(int wx, int wy, int rx, int ry, int gx, int gy, int bx, int by)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 } toString()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; PNGFilterInputStream(PNGImageDecoder owner, InputStream is)811 public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) { 812 super(is); 813 underlyingInputStream = in; 814 this.owner = owner; 815 } 816 available()817 public int available() throws IOException { 818 return owner.limit-owner.pos+in.available();} markSupported()819 public boolean markSupported() { return false; } read()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 } read(byte[] b)825 public int read(byte[] b) throws IOException{return read(b,0,b.length);} read(byte[] b, int st, int len)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 } skip(long n)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