blob: 0550eae666f03e182ae502102e070aea70314c43 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.image;
27
28import java.io.*;
29import java.util.*;
30import java.util.zip.*;
31import java.awt.image.*;
32import java.awt.Color;
33
34/** PNG - Portable Network Graphics - image file reader.
35 See <a href=ftp://ds.internic.net/rfc/rfc2083.txt>RFC2083</a> for details. */
36
37/* this is changed
38public class PNGImageDecoder extends FilterInputStream implements Runnable
39{ */
40
41public class PNGImageDecoder extends ImageDecoder
42{
43 private static final int GRAY=0;
44 private static final int PALETTE=1;
45 private static final int COLOR=2;
46 private static final int ALPHA=4;
47
48 private static final int bKGDChunk = 0x624B4744;
49 private static final int cHRMChunk = 0x6348524D;
50 private static final int gAMAChunk = 0x67414D41;
51 private static final int hISTChunk = 0x68495354;
52 private static final int IDATChunk = 0x49444154;
53 private static final int IENDChunk = 0x49454E44;
54 private static final int IHDRChunk = 0x49484452;
55 private static final int PLTEChunk = 0x504C5445;
56 private static final int pHYsChunk = 0x70485973;
57 private static final int sBITChunk = 0x73424954;
58 private static final int tEXtChunk = 0x74455874;
59 private static final int tIMEChunk = 0x74494D45;
60 private static final int tRNSChunk = 0x74524E53;
61 private static final int zTXtChunk = 0x7A545874;
62
63 private int width;
64 private int height;
65 private int bitDepth;
66 private int colorType;
67 private int compressionMethod;
68 private int filterMethod;
69 private int interlaceMethod;
70 private int gamma = 100000;
71 private java.util.Hashtable 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
807class 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}