001: /*
002: * @(#)PNGImageDecoder.java 1.17 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.awt.image;
029:
030: import java.io.*;
031: import java.util.*;
032: import java.util.zip.*;
033: import java.awt.image.*;
034: import java.awt.Color;
035:
036: /** PNG - Portable Network Graphics - image file reader.
037: See <a href=ftp://ds.internic.net/rfc/rfc2083.txt>RFC2083</a> for details. */
038:
039: /* this is changed
040: public class PNGImageDecoder extends FilterInputStream implements Runnable
041: { */
042:
043: public class PNGImageDecoder extends ImageDecoder {
044: private static final int GRAY = 0;
045: private static final int PALETTE = 1;
046: private static final int COLOR = 2;
047: private static final int ALPHA = 4;
048:
049: private static final int bKGDChunk = 0x624B4744;
050: private static final int cHRMChunk = 0x6348524D;
051: private static final int gAMAChunk = 0x67414D41;
052: private static final int hISTChunk = 0x68495354;
053: private static final int IDATChunk = 0x49444154;
054: private static final int IENDChunk = 0x49454E44;
055: private static final int IHDRChunk = 0x49484452;
056: private static final int PLTEChunk = 0x504C5445;
057: private static final int pHYsChunk = 0x70485973;
058: private static final int sBITChunk = 0x73424954;
059: private static final int tEXtChunk = 0x74455874;
060: private static final int tIMEChunk = 0x74494D45;
061: private static final int tRNSChunk = 0x74524E53;
062: private static final int zTXtChunk = 0x7A545874;
063:
064: private int width;
065: private int height;
066: private int bitDepth;
067: private int colorType;
068: private int compressionMethod;
069: private int filterMethod;
070: private int interlaceMethod;
071: private int gamma = 100000;
072: // 4679080 - create instance of properties as in JPEG and GIF image architecture.
073: private Hashtable properties = new Hashtable();
074: /* this is not needed
075: ImageConsumer target;
076: */
077: private ColorModel cm;
078: private byte[] red_map, green_map, blue_map, alpha_map;
079: private int transparentPixel = -1;
080: private static ColorModel greyModels[] = new ColorModel[4];
081:
082: /* this is not needed
083: PNGImageDecoder next;
084: */
085:
086: private void property(String key, Object value) {
087: if (value == null)
088: return;
089: properties.put(key, value);
090: }
091:
092: private void property(String key, float value) {
093: property(key, new Float(value));
094: }
095:
096: private final void verify(boolean b) throws IOException {
097: if (!b) {
098: PNGException e = new PNGException("Broken file");
099: e.printStackTrace();
100: throw e;
101: }
102: }
103:
104: protected boolean handleChunk(int key, byte[] buf, int st, int len)
105: throws IOException {
106: switch (key) {
107: case bKGDChunk:
108: Color c = null;
109: switch (colorType) {
110: case COLOR:
111: case COLOR | ALPHA:
112: verify(len == 6);
113: c = new Color(buf[st] & 0xff, buf[st + 2] & 0xff,
114: buf[st + 4] & 0xff);
115: break;
116:
117: case COLOR | PALETTE:
118: case COLOR | PALETTE | ALPHA:
119: verify(len == 1);
120: int ix = buf[st] & 0xFF;
121: verify(red_map != null && ix < red_map.length);
122: c = new Color(red_map[ix] & 0xff, green_map[ix] & 0xff,
123: blue_map[ix] & 0xff);
124: break;
125:
126: case GRAY:
127: case GRAY | ALPHA:
128: verify(len == 2);
129: int t = buf[st] & 0xFF;
130: c = new Color(t, t, t);
131: break;
132: }
133: if (c != null)
134: property("background", c);
135: break;
136:
137: case cHRMChunk:
138: property("chromaticities", new Chromaticities(getInt(st),
139: getInt(st + 4), getInt(st + 8), getInt(st + 12),
140: getInt(st + 16), getInt(st + 20), getInt(st + 24),
141: getInt(st + 28)));
142: break;
143:
144: case gAMAChunk:
145: if (len != 4)
146: throw new PNGException("bogus gAMA");
147: gamma = getInt(st);
148: if (gamma != 100000)
149: property("gamma", gamma / 100000.0f);
150: break;
151:
152: case hISTChunk:
153: break;
154:
155: case IDATChunk:
156: return false;
157:
158: case IENDChunk:
159: break;
160:
161: case IHDRChunk:
162: if (len != 13 || (width = getInt(st)) == 0
163: || (height = getInt(st + 4)) == 0)
164: throw new PNGException("bogus IHDR");
165: bitDepth = getByte(st + 8);
166: colorType = getByte(st + 9);
167: compressionMethod = getByte(st + 10);
168: filterMethod = getByte(st + 11);
169: interlaceMethod = getByte(st + 12);
170: /* this is not needed
171: if(target!=null) target.setDimensions(width,height);
172: */
173: break;
174:
175: case PLTEChunk: {
176: int tsize = len / 3;
177: red_map = new byte[tsize];
178: green_map = new byte[tsize];
179: blue_map = new byte[tsize];
180: for (int i = 0, j = st; i < tsize; i++, j += 3) {
181: red_map[i] = buf[j];
182: green_map[i] = buf[j + 1];
183: blue_map[i] = buf[j + 2];
184: }
185: }
186: break;
187:
188: case pHYsChunk:
189: break;
190:
191: case sBITChunk:
192: break;
193:
194: case tEXtChunk:
195: int klen = 0;
196: while (klen < len && buf[st + klen] != 0)
197: klen++;
198: if (klen < len) {
199: String tkey = new String(buf, st, klen);
200: String tvalue = new String(buf, st + klen + 1, len
201: - klen - 1);
202: property(tkey, tvalue);
203: }
204: break;
205:
206: case tIMEChunk:
207: property("modtime", new GregorianCalendar(getShort(st + 0),
208: getByte(st + 2) - 1, getByte(st + 3),
209: getByte(st + 4), getByte(st + 5), getByte(st + 6))
210: .getTime());
211: break;
212:
213: case tRNSChunk:
214: switch (colorType) {
215: case PALETTE | COLOR:
216: case PALETTE | COLOR | ALPHA:
217: int alen = len;
218: if (red_map != null)
219: alen = red_map.length;
220: alpha_map = new byte[alen];
221: System.arraycopy(buf, st, alpha_map, 0,
222: len < alen ? len : alen);
223: while (--alen >= len)
224: alpha_map[alen] = (byte) 0xFF;
225: break;
226:
227: case COLOR: // doesn't deal with 16 bit colors properly
228: case COLOR | ALPHA: // doesn't deal with 16 bit colors properly
229: verify(len == 6);
230: transparentPixel = ((buf[st + 0] & 0xFF) << 16)
231: | ((buf[st + 2] & 0xFF) << 8)
232: | ((buf[st + 4] & 0xFF));
233: break;
234:
235: case GRAY: // doesn't deal with 16 bit colors properly
236: case GRAY | ALPHA: // doesn't deal with 16 bit colors properly
237: verify(len == 2);
238: int t = buf[st] & 0xFF;
239: transparentPixel = (t << 16) | (t << 8) | t;
240: break;
241: }
242: break;
243:
244: case zTXtChunk:
245: break;
246: }
247: return true;
248: }
249:
250: public class PNGException extends IOException {
251: PNGException(String s) {
252: super (s);
253: }
254: }
255:
256: /* this is changed
257: public void run() {
258: */
259: public void produceImage() throws IOException, ImageFormatException {
260: /* this is not needed
261: ImageConsumer t = target;
262: if(t!=null) try {
263: */
264: try {
265: for (int i = 0; i < signature.length; i++)
266: if ((signature[i] & 0xFF) != underlyingInputStream
267: .read())
268: throw new PNGException("Chunk signature mismatch");
269: InputStream is = new BufferedInputStream(
270: new InflaterInputStream(inputStream, new Inflater()));
271: getData();
272: byte[] bPixels = null;
273: int[] wPixels = null;
274: int pixSize = width;
275: int rowStride;
276: int logDepth = 0;
277: switch (bitDepth) {
278: case 1:
279: logDepth = 0;
280: break;
281:
282: case 2:
283: logDepth = 1;
284: break;
285:
286: case 4:
287: logDepth = 2;
288: break;
289:
290: case 8:
291: logDepth = 3;
292: break;
293:
294: case 16:
295: logDepth = 4;
296: break;
297:
298: default:
299: throw new PNGException("invalid depth");
300: }
301: if (interlaceMethod != 0) {
302: pixSize *= height;
303: rowStride = width;
304: } else
305: rowStride = 0;
306: int combinedType = colorType | (bitDepth << 3);
307: int bitMask = (1 << (bitDepth >= 8 ? 8 : bitDepth)) - 1;
308: //Figure out the color model
309: switch (colorType) {
310: case COLOR | PALETTE:
311: case COLOR | PALETTE | ALPHA:
312: if (red_map == null)
313: throw new PNGException("palette expected");
314: if (alpha_map == null)
315: cm = new IndexColorModel(bitDepth, red_map.length,
316: red_map, green_map, blue_map);
317: else
318: cm = new IndexColorModel(bitDepth, red_map.length,
319: red_map, green_map, blue_map, alpha_map);
320: bPixels = new byte[pixSize];
321: break;
322:
323: case GRAY: {
324: int llog = logDepth >= 4 ? 3 : logDepth;
325: if ((cm = greyModels[llog]) == null) {
326: int size = 1 << (1 << llog);
327: byte ramp[] = new byte[size];
328: for (int i = 0; i < size; i++)
329: ramp[i] = (byte) (255 * i / (size - 1));
330: cm = new IndexColorModel(bitDepth, ramp.length,
331: ramp, ramp, ramp);
332: greyModels[llog] = cm;
333: }
334: }
335: bPixels = new byte[pixSize];
336: break;
337:
338: case COLOR:
339: case COLOR | ALPHA:
340: case GRAY | ALPHA:
341: cm = ColorModel.getRGBdefault();
342: wPixels = new int[pixSize];
343: break;
344:
345: default:
346: throw new PNGException("invalid color type");
347: }
348: /* this is going to be set in the pixel store
349: t.setColorModel(cm);
350: t.setHints(interlaceMethod !=0
351: ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
352: : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
353: ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
354: */
355: // code added to make it work with ImageDecoder architecture
356: setDimensions(width, height);
357: setColorModel(cm);
358: // 4679080 - set properties as in JPEG and GIF image architecture.
359: setProperties(properties);
360: // end fix - 4679080
361:
362: int flags = (interlaceMethod != 0 ? ImageConsumer.TOPDOWNLEFTRIGHT
363: | ImageConsumer.COMPLETESCANLINES
364: : ImageConsumer.TOPDOWNLEFTRIGHT
365: | ImageConsumer.COMPLETESCANLINES
366: | ImageConsumer.SINGLEPASS
367: | ImageConsumer.SINGLEFRAME);
368: setHints(flags);
369: headerComplete();
370: // end of adding
371:
372: int samplesPerPixel = ((colorType & PALETTE) != 0 ? 1
373: : ((colorType & COLOR) != 0 ? 3 : 1)
374: + ((colorType & ALPHA) != 0 ? 1 : 0));
375: int bitsPerPixel = samplesPerPixel * bitDepth;
376: int bytesPerPixel = (bitsPerPixel + 7) >> 3;
377: int pass, passLimit;
378: if (interlaceMethod == 0) {
379: pass = -1;
380: passLimit = 0;
381: } else {
382: pass = 0;
383: passLimit = 7;
384: }
385: // These loops are far from being tuned. They're this way to make them easy to
386: // debug. Tuning comes later.
387: /* code changed. target not needed here
388: while(++pass<=passLimit && (t=target)!=null) {
389: */
390: while (++pass <= passLimit) {
391: int row = startingRow[pass];
392: int rowInc = rowIncrement[pass];
393: int colInc = colIncrement[pass];
394: //int bWidth = blockWidth[pass];
395: //int bHeight = blockHeight[pass];
396: int sCol = startingCol[pass];
397: int rowPixelWidth = (width - sCol + (colInc - 1))
398: / colInc;
399: int rowByteWidth = ((rowPixelWidth * bitsPerPixel) + 7) >> 3;
400: if (rowByteWidth == 0)
401: continue;
402: int rowOffset = rowStride * row;
403: boolean firstRow = true;
404:
405: byte[] rowByteBuffer = new byte[rowByteWidth];
406: byte[] prevRowByteBuffer = new byte[rowByteWidth];
407: /* code changed. target not needed here
408: while (row < height && (t=target)!=null) {
409: */
410: while (row < height) {
411: int rowFilter = is.read();
412: for (int rowFillPos = 0; rowFillPos < rowByteWidth;) {
413: int n = is.read(rowByteBuffer, rowFillPos,
414: rowByteWidth - rowFillPos);
415: if (n <= 0)
416: throw new PNGException("missing data");
417: rowFillPos += n;
418: }
419: filterRow(rowByteBuffer, firstRow ? null
420: : prevRowByteBuffer, rowFilter,
421: rowByteWidth, bytesPerPixel);
422: int col = sCol;
423: int spos = 0;
424: while (col < width) {
425: if (wPixels != null) {
426: switch (combinedType) {
427: case COLOR | ALPHA | (8 << 3):
428: wPixels[col + rowOffset] = ((rowByteBuffer[spos] & 0xFF) << 16)
429: | ((rowByteBuffer[spos + 1] & 0xFF) << 8)
430: | ((rowByteBuffer[spos + 2] & 0xFF))
431: | ((rowByteBuffer[spos + 3] & 0xFF) << 24);
432: spos += 4;
433: break;
434:
435: case COLOR | ALPHA | (16 << 3):
436: wPixels[col + rowOffset] = ((rowByteBuffer[spos] & 0xFF) << 16)
437: | ((rowByteBuffer[spos + 2] & 0xFF) << 8)
438: | ((rowByteBuffer[spos + 4] & 0xFF))
439: | ((rowByteBuffer[spos + 6] & 0xFF) << 24);
440: spos += 8;
441: break;
442:
443: case COLOR | (8 << 3):
444: wPixels[col + rowOffset] = ((rowByteBuffer[spos] & 0xFF) << 16)
445: | ((rowByteBuffer[spos + 1] & 0xFF) << 8)
446: | ((rowByteBuffer[spos + 2] & 0xFF))
447: | ((0xFF) << 24);
448: spos += 3;
449: break;
450:
451: case COLOR | (16 << 3):
452: wPixels[col + rowOffset] = ((rowByteBuffer[spos] & 0xFF) << 16)
453: | ((rowByteBuffer[spos + 2] & 0xFF) << 8)
454: | ((rowByteBuffer[spos + 4] & 0xFF))
455: | ((0xFF) << 24);
456: spos += 6;
457: break;
458:
459: case GRAY | ALPHA | (8 << 3): {
460: int tx = rowByteBuffer[spos] & 0xFF;
461: wPixels[col + rowOffset] = (tx << 16)
462: | (tx << 8)
463: | tx
464: | ((rowByteBuffer[spos + 1] & 0xFF) << 24);
465: }
466: spos += 2;
467: break;
468:
469: case GRAY | ALPHA | (16 << 3): {
470: int tx = rowByteBuffer[spos] & 0xFF;
471: wPixels[col + rowOffset] = (tx << 16)
472: | (tx << 8)
473: | tx
474: | ((rowByteBuffer[spos + 2] & 0xFF) << 24);
475: }
476: spos += 4;
477: break;
478:
479: default:
480: throw new PNGException(
481: "illegal type/depth");
482: }
483: } else
484: switch (bitDepth) {
485: case 1:
486: bPixels[col + rowOffset] = (byte) ((rowByteBuffer[spos >> 3] >> (7 - (spos & 7))) & 1);
487: spos++;
488: break;
489:
490: case 2:
491: bPixels[col + rowOffset] = (byte) ((rowByteBuffer[spos >> 2] >> ((3 - (spos & 3)) * 2)) & 3);
492: spos++;
493: break;
494:
495: case 4:
496: bPixels[col + rowOffset] = (byte) ((rowByteBuffer[spos >> 1] >> ((1 - (spos & 1)) * 4)) & 7);
497: spos++;
498: break;
499:
500: case 8:
501: bPixels[col + rowOffset] = rowByteBuffer[spos++];
502: break;
503:
504: case 16:
505: bPixels[col + rowOffset] = rowByteBuffer[spos];
506: spos += 2;
507: break;
508:
509: default:
510: throw new PNGException(
511: "illegal type/depth");
512: }
513: /*visit (row, col,
514: min (bHeight, height - row),
515: min (bWidth, width - col)); */
516: col += colInc;
517: }
518: if (interlaceMethod == 0)
519: if (wPixels != null) {
520: /* code changed. target not needed here
521: t.setPixels(0,row,width,1,cm,wPixels,0,width);
522: */
523: // code added to make it work with ImageDecoder arch
524: sendPixels(0, row, width, 1, wPixels, 0,
525: width);
526: // end of adding
527: } else {
528: /* code changed. target not needed here
529: t.setPixels(0,row,width,1,cm,bPixels,0,width);
530: */
531: // code added to make it work with ImageDecoder arch
532: sendPixels(0, row, width, 1, bPixels, 0,
533: width);
534: //end of adding
535: }
536: row += rowInc;
537: rowOffset += rowInc * rowStride;
538: byte[] T = rowByteBuffer;
539: rowByteBuffer = prevRowByteBuffer;
540: prevRowByteBuffer = T;
541: firstRow = false;
542: }
543: if (interlaceMethod != 0)
544: if (wPixels != null) {
545: /* code changed. target not needed here
546: t.setPixels(0,0,width,height,cm,wPixels,0,width);
547: */
548: // code added to make it work with ImageDecoder arch
549: sendPixels(0, 0, width, height, wPixels, 0,
550: width);
551: //end of adding
552: } else {
553: /* code changed. target not needed here
554: t.setPixels(0,0,width,height,cm,bPixels,0,width);
555: */
556: // code added to make it work with ImageDecoder arch
557: sendPixels(0, 0, width, height, bPixels, 0,
558: width);
559: //end of adding
560: }
561: }
562: /* Here, the function "visit(row,column,height,width)" obtains the
563: next transmitted pixel and paints a rectangle of the specified
564: height and width, whose upper-left corner is at the specified row
565: and column, using the color indicated by the pixel. Note that row
566: and column are measured from 0,0 at the upper left corner. */
567:
568: /* code not needed, don't deal with target
569: if((t=target)!=null) {
570: if(properties!=null) t.setProperties(properties);
571: t.imageComplete(ImageConsumer.STATICIMAGEDONE);
572: */
573:
574: imageComplete(ImageConsumer.STATICIMAGEDONE, true);
575: /* code not needed }
576: is.close();
577: */
578: } catch (Throwable e) {
579: /* code not needed
580: if((t=target)!=null) {
581: PNGEncoder.prChunk(e.toString(),inbuf,pos,limit-pos,true);
582: */
583: property("error", e);
584: /* code not needed
585: t.setProperties(properties);
586: t.imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE);
587: */
588: imageComplete(ImageConsumer.IMAGEERROR
589: | ImageConsumer.STATICIMAGEDONE, true);
590: // }
591: e.printStackTrace();
592: } finally {
593: try {
594: close();
595: } catch (Throwable e) {
596: }
597: /* code not needed
598: target = null;
599: endTurn();
600: */
601: }
602: }
603:
604: private boolean sendPixels(int x, int y, int w, int h,
605: int[] pixels, int offset, int pixlength) {
606: int count = setPixels(x, y, w, h, cm, pixels, offset, pixlength);
607: if (count <= 0) {
608: aborted = true;
609: }
610: return !aborted;
611: }
612:
613: private boolean sendPixels(int x, int y, int w, int h,
614: byte[] pixels, int offset, int pixlength) {
615: int count = setPixels(x, y, w, h, cm, pixels, offset, pixlength);
616: if (count <= 0) {
617: aborted = true;
618: }
619: return !aborted;
620: }
621:
622: private void filterRow(byte rowByteBuffer[], byte[] prevRow,
623: int rowFilter, int rowByteWidth, int bytesPerSample)
624: throws IOException {
625: int x = 0;
626: switch (rowFilter) {
627: case 0:
628: break;
629:
630: case 1:
631: for (x = bytesPerSample; x < rowByteWidth; x++)
632: rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
633: break;
634:
635: case 2:
636: if (prevRow != null)
637: for (; x < rowByteWidth; x++)
638: rowByteBuffer[x] += prevRow[x];
639: break;
640:
641: case 3:
642: if (prevRow != null) {
643: for (; x < bytesPerSample; x++)
644: rowByteBuffer[x] += (0xff & prevRow[x]) >> 1;
645: for (; x < rowByteWidth; x++)
646: rowByteBuffer[x] += ((prevRow[x] & 0xFF) + (rowByteBuffer[x
647: - bytesPerSample] & 0xFF)) >> 1;
648: } else
649: for (x = bytesPerSample; x < rowByteWidth; x++)
650: rowByteBuffer[x] += (rowByteBuffer[x
651: - bytesPerSample] & 0xFF) >> 1;
652: break;
653:
654: case 4:
655: if (prevRow != null) {
656: for (; x < bytesPerSample; x++)
657: rowByteBuffer[x] += prevRow[x];
658: for (; x < rowByteWidth; x++) {
659: int a, b, c, p, pa, pb, pc;
660: a = rowByteBuffer[x - bytesPerSample] & 0xFF;
661: b = prevRow[x] & 0xFF;
662: c = prevRow[x - bytesPerSample] & 0xFF;
663: p = a + b - c;
664: pa = p > a ? p - a : a - p;
665: pb = p > b ? p - b : b - p;
666: pc = p > c ? p - c : c - p;
667: rowByteBuffer[x] += (pa <= pb) && (pa <= pc) ? a
668: : pb <= pc ? b : c;
669: }
670: } else
671: for (x = bytesPerSample; x < rowByteWidth; x++)
672: rowByteBuffer[x] += rowByteBuffer[x
673: - bytesPerSample];
674: break;
675:
676: default:
677: throw new PNGException("Illegal filter");
678: }
679: }
680:
681: private static final byte[] startingRow = { 0, 0, 0, 4, 0, 2, 0, 1 };
682: private static final byte[] startingCol = { 0, 0, 4, 0, 2, 0, 1, 0 };
683: private static final byte[] rowIncrement = { 1, 8, 8, 8, 4, 4, 2, 2 };
684: private static final byte[] colIncrement = { 1, 8, 8, 4, 4, 2, 2, 1 };
685: private static final byte[] blockHeight = { 1, 8, 8, 4, 4, 2, 2, 1 };
686: private static final byte[] blockWidth = { 1, 8, 4, 4, 2, 2, 1, 1 };
687:
688: //abstract public class ChunkReader extends FilterInputStream {
689: int pos, limit;
690: int chunkStart;
691: int chunkKey, chunkLength, chunkCRC;
692: boolean seenEOF;
693:
694: private static final byte[] signature = { (byte) 137, (byte) 80,
695: (byte) 78, (byte) 71, (byte) 13, (byte) 10, (byte) 26,
696: (byte) 10 };
697:
698: PNGFilterInputStream inputStream;
699: InputStream underlyingInputStream;
700:
701: /* code changed
702: public PNGImageDecoder(InputStream in, ImageConsumer t) throws IOException {
703: */
704: public PNGImageDecoder(InputStreamImageSource src, InputStream input)
705: throws IOException {
706: // code added
707: super (src, input);
708: inputStream = new PNGFilterInputStream(this , input);
709: underlyingInputStream = inputStream.underlyingInputStream;
710: // end of adding
711: /* code changed
712: super(in);
713: target = t;
714: waitTurn();
715: new Thread(this).start();
716: */
717: }
718:
719: /* code changed to make it work with ImageDecoder architecture
720: static int ThreadLimit = 10;
721: private synchronized static void waitTurn() {
722: try {
723: while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000);
724: } catch(InterruptedException e){}
725: ThreadLimit--;
726: }
727: private synchronized static void endTurn() {
728: if(ThreadLimit<=0) PNGImageDecoder.class.notify();
729: ThreadLimit++;
730: }
731: */
732: byte[] inbuf = new byte[4096];
733:
734: private void fill() throws IOException {
735: if (!seenEOF) {
736: if (pos > 0 && pos < limit) {
737: System.arraycopy(inbuf, pos, inbuf, 0, limit - pos);
738: limit = limit - pos;
739: pos = 0;
740: } else if (pos >= limit) {
741: pos = 0;
742: limit = 0;
743: }
744: int bsize = inbuf.length;
745: while (limit < bsize) {
746: int n = underlyingInputStream.read(inbuf, limit, bsize
747: - limit);
748: if (n <= 0) {
749: seenEOF = true;
750: break;
751: }
752: limit += n;
753: }
754: }
755: }
756:
757: private boolean need(int n) throws IOException {
758: if (limit - pos >= n)
759: return true;
760: fill();
761: if (limit - pos >= n)
762: return true;
763: if (seenEOF)
764: return false;
765: byte nin[] = new byte[n + 100];
766: System.arraycopy(inbuf, pos, nin, 0, limit - pos);
767: limit = limit - pos;
768: pos = 0;
769: inbuf = nin;
770: fill();
771: return limit - pos >= n;
772: }
773:
774: private final int getInt(int pos) {
775: return ((inbuf[pos] & 0xFF) << 24)
776: | ((inbuf[pos + 1] & 0xFF) << 16)
777: | ((inbuf[pos + 2] & 0xFF) << 8)
778: | ((inbuf[pos + 3] & 0xFF));
779: }
780:
781: private final int getShort(int pos) {
782: return (short) (((inbuf[pos] & 0xFF) << 8) | ((inbuf[pos + 1] & 0xFF)));
783: }
784:
785: private final int getByte(int pos) {
786: return inbuf[pos] & 0xFF;
787: }
788:
789: private final boolean getChunk() throws IOException {
790: chunkLength = 0;
791: if (!need(8))
792: return false;
793: chunkLength = getInt(pos);
794: chunkKey = getInt(pos + 4);
795: if (chunkLength < 0)
796: throw new PNGException("bogus length: " + chunkLength);
797: if (!need(chunkLength + 12))
798: return false;
799: chunkCRC = getInt(pos + 8 + chunkLength);
800: chunkStart = pos + 8;
801: int calcCRC = crc(inbuf, pos + 4, chunkLength + 4);
802: if (chunkCRC != calcCRC && checkCRC)
803: throw new PNGException("crc corruption");
804: pos += chunkLength + 12;
805: return true;
806: }
807:
808: private void readAll() throws IOException {
809: while (getChunk())
810: handleChunk(chunkKey, inbuf, chunkStart, chunkLength);
811: }
812:
813: boolean getData() throws IOException {
814: while (chunkLength == 0 && getChunk())
815: if (handleChunk(chunkKey, inbuf, chunkStart, chunkLength))
816: chunkLength = 0;
817: return chunkLength > 0;
818: }
819:
820: //abstract protected boolean handleChunk(int key, byte[] buf, int st, int len)
821: // throws IOException;
822: private static boolean checkCRC = true;
823:
824: public static boolean getCheckCRC() {
825: return checkCRC;
826: }
827:
828: public static void setCheckCRC(boolean c) {
829: checkCRC = c;
830: }
831:
832: protected void wrc(int c) {
833: c = c & 0xFF;
834: if (c <= ' ' || c > 'z')
835: c = '?';
836: System.out.write(c);
837: }
838:
839: protected void wrk(int n) {
840: wrc(n >> 24);
841: wrc(n >> 16);
842: wrc(n >> 8);
843: wrc(n);
844: }
845:
846: public void print() {
847: wrk(chunkKey);
848: System.out.print(" " + chunkLength + "\n");
849: }
850:
851: /* Table of CRCs of all 8-bit messages. */
852: private static final int[] crc_table = new int[256];
853:
854: /* Make the table for a fast CRC. */
855: static {
856: for (int n = 0; n < 256; n++) {
857: int c = n;
858: for (int k = 0; k < 8; k++)
859: if ((c & 1) != 0)
860: c = 0xedb88320 ^ (c >>> 1);
861: else
862: c = c >>> 1;
863: crc_table[n] = c;
864: }
865: }
866:
867: /* Update a running CRC with the bytes buf[0..len-1]--the CRC
868: should be initialized to all 1's, and the transmitted value
869: is the 1's complement of the final running CRC (see the
870: crc() routine below)). */
871:
872: static private int update_crc(int crc, byte[] buf, int offset,
873: int len) {
874: int c = crc;
875: while (--len >= 0)
876: c = crc_table[(c ^ buf[offset++]) & 0xff] ^ (c >>> 8);
877: return c;
878: }
879:
880: /* Return the CRC of the bytes buf[0..len-1]. */
881: static private int crc(byte[] buf, int offset, int len) {
882: return update_crc(0xffffffff, buf, offset, len) ^ 0xffffffff;
883: }
884:
885: public static class Chromaticities {
886: public float whiteX, whiteY, redX, redY, greenX, greenY, blueX,
887: blueY;
888:
889: Chromaticities(int wx, int wy, int rx, int ry, int gx, int gy,
890: int bx, int by) {
891: whiteX = wx / 100000.0f;
892: whiteY = wy / 100000.0f;
893: redX = rx / 100000.0f;
894: redY = ry / 100000.0f;
895: greenX = gx / 100000.0f;
896: greenY = gy / 100000.0f;
897: blueX = bx / 100000.0f;
898: blueY = by / 100000.0f;
899: }
900:
901: public String toString() {
902: return "Chromaticities(white=" + whiteX + "," + whiteY
903: + ";red=" + redX + "," + redY + ";green=" + greenX
904: + "," + greenY + ";blue=" + blueX + "," + blueY
905: + ")";
906: }
907: }
908: }
909:
910: // the following class are added to make it work with ImageDecoder architecture
911:
912: class PNGFilterInputStream extends FilterInputStream {
913: PNGImageDecoder owner;
914: public InputStream underlyingInputStream;
915:
916: public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) {
917: super (is);
918: underlyingInputStream = in;
919: this .owner = owner;
920: }
921:
922: public int available() throws IOException {
923: return owner.limit - owner.pos + in.available();
924: }
925:
926: public boolean markSupported() {
927: return false;
928: }
929:
930: public int read() throws IOException {
931: if (owner.chunkLength <= 0)
932: if (!owner.getData())
933: return -1;
934: owner.chunkLength--;
935: return owner.inbuf[owner.chunkStart++] & 0xFF;
936: }
937:
938: public int read(byte[] b) throws IOException {
939: return read(b, 0, b.length);
940: }
941:
942: public int read(byte[] b, int st, int len) throws IOException {
943: if (owner.chunkLength <= 0)
944: if (!owner.getData())
945: return -1;
946: if (owner.chunkLength < len)
947: len = owner.chunkLength;
948: System.arraycopy(owner.inbuf, owner.chunkStart, b, st, len);
949: owner.chunkLength -= len;
950: owner.chunkStart += len;
951: return len;
952: }
953:
954: public long skip(long n) throws IOException {
955: int i;
956: for (i = 0; i < n && read() >= 0; i++)
957: ;
958: return i;
959: }
960: }
|