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