/*
* @(#)PNGImageDecoder.java 1.17 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package sun.awt.image;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.awt.image.*;
import java.awt.Color;
/** PNG - Portable Network Graphics - image file reader.
See <a href=ftp://ds.internic.net/rfc/rfc2083.txt>RFC2083</a> for details. */
/* this is changed
public class PNGImageDecoder extends FilterInputStream implements Runnable
{ */
public class PNGImageDecoder extends ImageDecoder
{
private static final int GRAY=0;
private static final int PALETTE=1;
private static final int COLOR=2;
private static final int ALPHA=4;
private static final int bKGDChunk = 0x624B4744;
private static final int cHRMChunk = 0x6348524D;
private static final int gAMAChunk = 0x67414D41;
private static final int hISTChunk = 0x68495354;
private static final int IDATChunk = 0x49444154;
private static final int IENDChunk = 0x49454E44;
private static final int IHDRChunk = 0x49484452;
private static final int PLTEChunk = 0x504C5445;
private static final int pHYsChunk = 0x70485973;
private static final int sBITChunk = 0x73424954;
private static final int tEXtChunk = 0x74455874;
private static final int tIMEChunk = 0x74494D45;
private static final int tRNSChunk = 0x74524E53;
private static final int zTXtChunk = 0x7A545874;
private int width;
private int height;
private int bitDepth;
private int colorType;
private int compressionMethod;
private int filterMethod;
private int interlaceMethod;
private int gamma = 100000;
// 4679080 - create instance of properties as in JPEG and GIF image architecture.
private Hashtable properties = new Hashtable();
/* this is not needed
ImageConsumer target;
*/
private ColorModel cm;
private byte[] red_map, green_map, blue_map, alpha_map;
private int transparentPixel = -1;
private static ColorModel greyModels[] = new ColorModel[4];
/* this is not needed
PNGImageDecoder next;
*/
private void property(String key, Object value) {
if (value == null) return;
properties.put(key, value);
}
private void property(String key, float value) {
property(key, new Float(value));
}
private final void verify(boolean b) throws IOException {
if (!b) {
PNGException e = new PNGException("Broken file");
e.printStackTrace();
throw e;
}
}
protected boolean handleChunk(int key, byte[] buf, int st, int len)
throws IOException {
switch (key) {
case bKGDChunk:
Color c = null;
switch (colorType) {
case COLOR:
case COLOR | ALPHA:
verify(len == 6);
c = new Color(buf[st] & 0xff, buf[st + 2] & 0xff, buf[st + 4] & 0xff);
break;
case COLOR | PALETTE:
case COLOR | PALETTE | ALPHA:
verify(len == 1);
int ix = buf[st] & 0xFF;
verify(red_map != null && ix < red_map.length);
c = new Color(red_map[ix] & 0xff, green_map[ix] & 0xff, blue_map[ix] & 0xff);
break;
case GRAY:
case GRAY | ALPHA:
verify(len == 2);
int t = buf[st] & 0xFF;
c = new Color(t, t, t);
break;
}
if (c != null) property("background", c);
break;
case cHRMChunk:
property("chromaticities",
new Chromaticities(
getInt(st),
getInt(st + 4),
getInt(st + 8),
getInt(st + 12),
getInt(st + 16),
getInt(st + 20),
getInt(st + 24),
getInt(st + 28)));
break;
case gAMAChunk:
if (len != 4) throw new PNGException("bogus gAMA");
gamma = getInt(st);
if (gamma != 100000) property("gamma", gamma / 100000.0f);
break;
case hISTChunk:
break;
case IDATChunk:
return false;
case IENDChunk:
break;
case IHDRChunk:
if (len != 13
|| (width = getInt(st)) == 0
|| (height = getInt(st + 4)) == 0
) throw new PNGException("bogus IHDR");
bitDepth = getByte(st + 8);
colorType = getByte(st + 9);
compressionMethod = getByte(st + 10);
filterMethod = getByte(st + 11);
interlaceMethod = getByte(st + 12);
/* this is not needed
if(target!=null) target.setDimensions(width,height);
*/
break;
case PLTEChunk: {
int tsize = len / 3;
red_map = new byte[tsize];
green_map = new byte[tsize];
blue_map = new byte[tsize];
for (int i = 0, j = st; i < tsize; i++, j += 3) {
red_map[i] = buf[j];
green_map[i] = buf[j + 1];
blue_map[i] = buf[j + 2];
}
}
break;
case pHYsChunk:
break;
case sBITChunk:
break;
case tEXtChunk:
int klen = 0;
while (klen < len && buf[st + klen] != 0) klen++;
if (klen < len) {
String tkey = new String(buf, st, klen);
String tvalue = new String(buf, st + klen + 1, len - klen - 1);
property(tkey, tvalue);
}
break;
case tIMEChunk:
property("modtime", new GregorianCalendar(
getShort(st + 0),
getByte(st + 2) - 1,
getByte(st + 3),
getByte(st + 4),
getByte(st + 5),
getByte(st + 6)).getTime());
break;
case tRNSChunk:
switch (colorType) {
case PALETTE | COLOR:
case PALETTE | COLOR | ALPHA:
int alen = len;
if (red_map != null) alen = red_map.length;
alpha_map = new byte[alen];
System.arraycopy(buf, st, alpha_map, 0, len < alen ? len : alen);
while (--alen >= len) alpha_map[alen] = (byte) 0xFF;
break;
case COLOR: // doesn't deal with 16 bit colors properly
case COLOR | ALPHA: // doesn't deal with 16 bit colors properly
verify(len == 6);
transparentPixel =
((buf[st + 0] & 0xFF) << 16)
| ((buf[st + 2] & 0xFF) << 8)
| ((buf[st + 4] & 0xFF));
break;
case GRAY: // doesn't deal with 16 bit colors properly
case GRAY | ALPHA: // doesn't deal with 16 bit colors properly
verify(len == 2);
int t = buf[st] & 0xFF;
transparentPixel = (t << 16) | (t << 8) | t;
break;
}
break;
case zTXtChunk:
break;
}
return true;
}
public class PNGException extends IOException {
PNGException(String s) {
super(s);
}
}
/* this is changed
public void run() {
*/
public void produceImage() throws IOException, ImageFormatException {
/* this is not needed
ImageConsumer t = target;
if(t!=null) try {
*/
try {
for (int i = 0; i < signature.length; i++)
if ((signature[i] & 0xFF) != underlyingInputStream.read())
throw new PNGException("Chunk signature mismatch");
InputStream is = new BufferedInputStream(new InflaterInputStream(inputStream, new Inflater()));
getData();
byte[] bPixels = null;
int[] wPixels = null;
int pixSize = width;
int rowStride;
int logDepth = 0;
switch (bitDepth) {
case 1:
logDepth = 0;
break;
case 2:
logDepth = 1;
break;
case 4:
logDepth = 2;
break;
case 8:
logDepth = 3;
break;
case 16:
logDepth = 4;
break;
default:
throw new PNGException("invalid depth");
}
if (interlaceMethod != 0) {
pixSize *= height;
rowStride = width;
} else rowStride = 0;
int combinedType = colorType | (bitDepth << 3);
int bitMask = (1 << (bitDepth >= 8 ? 8 : bitDepth)) - 1;
//Figure out the color model
switch (colorType) {
case COLOR | PALETTE:
case COLOR | PALETTE | ALPHA:
if (red_map == null) throw new PNGException("palette expected");
if (alpha_map == null)
cm = new IndexColorModel(bitDepth, red_map.length,
red_map, green_map, blue_map);
else
cm = new IndexColorModel(bitDepth, red_map.length,
red_map, green_map, blue_map, alpha_map);
bPixels = new byte[pixSize];
break;
case GRAY: {
int llog = logDepth >= 4 ? 3 : logDepth;
if ((cm = greyModels[llog]) == null) {
int size = 1 << (1 << llog);
byte ramp[] = new byte[size];
for (int i = 0; i < size; i++) ramp[i] = (byte) (255 * i / (size - 1));
cm = new IndexColorModel(bitDepth, ramp.length, ramp, ramp, ramp);
greyModels[llog] = cm;
}
}
bPixels = new byte[pixSize];
break;
case COLOR:
case COLOR | ALPHA:
case GRAY | ALPHA:
cm = ColorModel.getRGBdefault();
wPixels = new int[pixSize];
break;
default:
throw new PNGException("invalid color type");
}
/* this is going to be set in the pixel store
t.setColorModel(cm);
t.setHints(interlaceMethod !=0
? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
: ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
*/
// code added to make it work with ImageDecoder architecture
setDimensions(width, height);
setColorModel(cm);
// 4679080 - set properties as in JPEG and GIF image architecture.
setProperties(properties);
// end fix - 4679080
int flags = (interlaceMethod != 0
? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES
: ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);
setHints(flags);
headerComplete();
// end of adding
int samplesPerPixel = ((colorType & PALETTE) != 0 ? 1
: ((colorType & COLOR) != 0 ? 3 : 1) + ((colorType & ALPHA) != 0 ? 1 : 0));
int bitsPerPixel = samplesPerPixel * bitDepth;
int bytesPerPixel = (bitsPerPixel + 7) >> 3;
int pass, passLimit;
if (interlaceMethod == 0) {
pass = -1;
passLimit = 0;
} else {
pass = 0;
passLimit = 7;
}
// These loops are far from being tuned. They're this way to make them easy to
// debug. Tuning comes later.
/* code changed. target not needed here
while(++pass<=passLimit && (t=target)!=null) {
*/
while (++pass <= passLimit) {
int row = startingRow[pass];
int rowInc = rowIncrement[pass];
int colInc = colIncrement[pass];
//int bWidth = blockWidth[pass];
//int bHeight = blockHeight[pass];
int sCol = startingCol[pass];
int rowPixelWidth = (width - sCol + (colInc - 1)) / colInc;
int rowByteWidth = ((rowPixelWidth * bitsPerPixel) + 7) >> 3;
if (rowByteWidth == 0) continue;
int rowOffset = rowStride * row;
boolean firstRow = true;
byte[] rowByteBuffer = new byte[rowByteWidth];
byte[] prevRowByteBuffer = new byte[rowByteWidth];
/* code changed. target not needed here
while (row < height && (t=target)!=null) {
*/
while (row < height) {
int rowFilter = is.read();
for (int rowFillPos = 0; rowFillPos < rowByteWidth;) {
int n = is.read(rowByteBuffer, rowFillPos, rowByteWidth - rowFillPos);
if (n <= 0) throw new PNGException("missing data");
rowFillPos += n;
}
filterRow(rowByteBuffer,
firstRow ? null : prevRowByteBuffer,
rowFilter, rowByteWidth, bytesPerPixel);
int col = sCol;
int spos = 0;
while (col < width) {
if (wPixels != null) {
switch (combinedType) {
case COLOR | ALPHA | (8 << 3):
wPixels[col + rowOffset] =
((rowByteBuffer[spos ] & 0xFF) << 16)
| ((rowByteBuffer[spos + 1] & 0xFF) << 8)
| ((rowByteBuffer[spos + 2] & 0xFF))
| ((rowByteBuffer[spos + 3] & 0xFF) << 24);
spos += 4;
break;
case COLOR | ALPHA | (16 << 3):
wPixels[col + rowOffset] =
((rowByteBuffer[spos ] & 0xFF) << 16)
| ((rowByteBuffer[spos + 2] & 0xFF) << 8)
| ((rowByteBuffer[spos + 4] & 0xFF))
| ((rowByteBuffer[spos + 6] & 0xFF) << 24);
spos += 8;
break;
case COLOR | (8 << 3):
wPixels[col + rowOffset] =
((rowByteBuffer[spos ] & 0xFF) << 16)
| ((rowByteBuffer[spos + 1] & 0xFF) << 8)
| ((rowByteBuffer[spos + 2] & 0xFF))
| ((0xFF) << 24);
spos += 3;
break;
case COLOR | (16 << 3):
wPixels[col + rowOffset] =
((rowByteBuffer[spos ] & 0xFF) << 16)
| ((rowByteBuffer[spos + 2] & 0xFF) << 8)
| ((rowByteBuffer[spos + 4] & 0xFF))
| ((0xFF) << 24);
spos += 6;
break;
case GRAY | ALPHA | (8 << 3): {
int tx = rowByteBuffer[spos] & 0xFF;
wPixels[col + rowOffset] =
(tx << 16) | (tx << 8) | tx
| ((rowByteBuffer[spos + 1] & 0xFF) << 24);
}
spos += 2;
break;
case GRAY | ALPHA | (16 << 3): {
int tx = rowByteBuffer[spos] & 0xFF;
wPixels[col + rowOffset] =
(tx << 16) | (tx << 8) | tx
| ((rowByteBuffer[spos + 2] & 0xFF) << 24);
}
spos += 4;
break;
default:
throw new PNGException("illegal type/depth");
}
} else switch (bitDepth) {
case 1:
bPixels[col + rowOffset] =
(byte) ((rowByteBuffer[spos >> 3] >> (7 - (spos & 7))) & 1);
spos++;
break;
case 2:
bPixels[col + rowOffset] =
(byte) ((rowByteBuffer[spos >> 2] >> ((3 - (spos & 3)) * 2)) & 3);
spos++;
break;
case 4:
bPixels[col + rowOffset] =
(byte) ((rowByteBuffer[spos >> 1] >> ((1 - (spos & 1)) * 4)) & 7);
spos++;
break;
case 8:
bPixels[col + rowOffset] = rowByteBuffer[spos++];
break;
case 16:
bPixels[col + rowOffset] = rowByteBuffer[spos];
spos += 2;
break;
default:
throw new PNGException("illegal type/depth");
}
/*visit (row, col,
min (bHeight, height - row),
min (bWidth, width - col)); */
col += colInc;
}
if (interlaceMethod == 0)
if (wPixels != null) {
/* code changed. target not needed here
t.setPixels(0,row,width,1,cm,wPixels,0,width);
*/
// code added to make it work with ImageDecoder arch
sendPixels(0, row, width, 1, wPixels, 0, width);
// end of adding
} else {
/* code changed. target not needed here
t.setPixels(0,row,width,1,cm,bPixels,0,width);
*/
// code added to make it work with ImageDecoder arch
sendPixels(0, row, width, 1, bPixels, 0, width);
//end of adding
}
row += rowInc;
rowOffset += rowInc * rowStride;
byte[] T = rowByteBuffer;
rowByteBuffer = prevRowByteBuffer;
prevRowByteBuffer = T;
firstRow = false;
}
if (interlaceMethod != 0)
if (wPixels != null) {
/* code changed. target not needed here
t.setPixels(0,0,width,height,cm,wPixels,0,width);
*/
// code added to make it work with ImageDecoder arch
sendPixels(0, 0, width, height, wPixels, 0, width);
//end of adding
} else {
/* code changed. target not needed here
t.setPixels(0,0,width,height,cm,bPixels,0,width);
*/
// code added to make it work with ImageDecoder arch
sendPixels(0, 0, width, height, bPixels, 0, width);
//end of adding
}
}
/* Here, the function "visit(row,column,height,width)" obtains the
next transmitted pixel and paints a rectangle of the specified
height and width, whose upper-left corner is at the specified row
and column, using the color indicated by the pixel. Note that row
and column are measured from 0,0 at the upper left corner. */
/* code not needed, don't deal with target
if((t=target)!=null) {
if(properties!=null) t.setProperties(properties);
t.imageComplete(ImageConsumer.STATICIMAGEDONE);
*/
imageComplete(ImageConsumer.STATICIMAGEDONE, true);
/* code not needed }
is.close();
*/
} catch (Throwable e) {
/* code not needed
if((t=target)!=null) {
PNGEncoder.prChunk(e.toString(),inbuf,pos,limit-pos,true);
*/
property("error", e);
/* code not needed
t.setProperties(properties);
t.imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE);
*/
imageComplete(ImageConsumer.IMAGEERROR | ImageConsumer.STATICIMAGEDONE, true);
// }
e.printStackTrace();
} finally {
try {
close();
} catch (Throwable e) {}
/* code not needed
target = null;
endTurn();
*/
}
}
private boolean sendPixels(int x, int y, int w, int h, int[] pixels,
int offset, int pixlength) {
int count = setPixels(x, y, w, h, cm,
pixels, offset, pixlength);
if (count <= 0) {
aborted = true;
}
return !aborted;
}
private boolean sendPixels(int x, int y, int w, int h, byte[] pixels,
int offset, int pixlength) {
int count = setPixels(x, y, w, h, cm,
pixels, offset, pixlength);
if (count <= 0) {
aborted = true;
}
return !aborted;
}
private void filterRow(byte rowByteBuffer[], byte[] prevRow,
int rowFilter, int rowByteWidth, int bytesPerSample)
throws IOException {
int x = 0;
switch (rowFilter) {
case 0:
break;
case 1:
for (x = bytesPerSample; x < rowByteWidth; x++)
rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
break;
case 2:
if (prevRow != null)
for (; x < rowByteWidth; x++)
rowByteBuffer[x] += prevRow[x];
break;
case 3:
if (prevRow != null) {
for (; x < bytesPerSample; x++)
rowByteBuffer[x] += (0xff & prevRow[x]) >> 1;
for (; x < rowByteWidth; x++)
rowByteBuffer[x] += ((prevRow[x] & 0xFF) + (rowByteBuffer[x - bytesPerSample] & 0xFF)) >> 1;
} else
for (x = bytesPerSample; x < rowByteWidth; x++)
rowByteBuffer[x] += (rowByteBuffer[x - bytesPerSample] & 0xFF) >> 1;
break;
case 4:
if (prevRow != null) {
for (; x < bytesPerSample; x++)
rowByteBuffer[x] += prevRow[x];
for (; x < rowByteWidth; x++) {
int a, b, c, p, pa, pb, pc;
a = rowByteBuffer[x - bytesPerSample] & 0xFF;
b = prevRow[x] & 0xFF;
c = prevRow[x - bytesPerSample] & 0xFF;
p = a + b - c;
pa = p > a ? p - a : a - p;
pb = p > b ? p - b : b - p;
pc = p > c ? p - c : c - p;
rowByteBuffer[x] += (pa <= pb) && (pa <= pc) ? a : pb <= pc ? b : c;
}
} else
for (x = bytesPerSample; x < rowByteWidth; x++)
rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample];
break;
default:
throw new PNGException("Illegal filter");
}
}
private static final byte[] startingRow = { 0, 0, 0, 4, 0, 2, 0, 1 };
private static final byte[] startingCol = { 0, 0, 4, 0, 2, 0, 1, 0 };
private static final byte[] rowIncrement = { 1, 8, 8, 8, 4, 4, 2, 2 };
private static final byte[] colIncrement = { 1, 8, 8, 4, 4, 2, 2, 1 };
private static final byte[] blockHeight = { 1, 8, 8, 4, 4, 2, 2, 1 };
private static final byte[] blockWidth = { 1, 8, 4, 4, 2, 2, 1, 1 };
//abstract public class ChunkReader extends FilterInputStream {
int pos, limit;
int chunkStart;
int chunkKey, chunkLength, chunkCRC;
boolean seenEOF;
private static final byte[] signature = { (byte) 137, (byte) 80, (byte) 78,
(byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
PNGFilterInputStream inputStream;
InputStream underlyingInputStream;
/* code changed
public PNGImageDecoder(InputStream in, ImageConsumer t) throws IOException {
*/
public PNGImageDecoder(InputStreamImageSource src, InputStream input) throws IOException {
// code added
super(src, input);
inputStream = new PNGFilterInputStream(this, input);
underlyingInputStream = inputStream.underlyingInputStream;
// end of adding
/* code changed
super(in);
target = t;
waitTurn();
new Thread(this).start();
*/
}
/* code changed to make it work with ImageDecoder architecture
static int ThreadLimit = 10;
private synchronized static void waitTurn() {
try {
while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000);
} catch(InterruptedException e){}
ThreadLimit--;
}
private synchronized static void endTurn() {
if(ThreadLimit<=0) PNGImageDecoder.class.notify();
ThreadLimit++;
}
*/
byte[] inbuf = new byte[4096];
private void fill() throws IOException {
if (!seenEOF) {
if (pos > 0 && pos < limit) {
System.arraycopy(inbuf, pos, inbuf, 0, limit - pos);
limit = limit - pos;
pos = 0;
} else if (pos >= limit) {
pos = 0;
limit = 0;
}
int bsize = inbuf.length;
while (limit < bsize) {
int n = underlyingInputStream.read(inbuf, limit, bsize - limit);
if (n <= 0) {
seenEOF = true;
break;
}
limit += n;
}
}
}
private boolean need(int n) throws IOException {
if (limit - pos >= n) return true;
fill();
if (limit - pos >= n) return true;
if (seenEOF) return false;
byte nin[] = new byte[n + 100];
System.arraycopy(inbuf, pos, nin, 0, limit - pos);
limit = limit - pos;
pos = 0;
inbuf = nin;
fill();
return limit - pos >= n;
}
private final int getInt(int pos) {
return ((inbuf[pos ] & 0xFF) << 24)
| ((inbuf[pos + 1] & 0xFF) << 16)
| ((inbuf[pos + 2] & 0xFF) << 8)
| ((inbuf[pos + 3] & 0xFF));
}
private final int getShort(int pos) {
return (short) (((inbuf[pos ] & 0xFF) << 8)
| ((inbuf[pos + 1] & 0xFF)));
}
private final int getByte(int pos) {
return inbuf[pos] & 0xFF;
}
private final boolean getChunk() throws IOException {
chunkLength = 0;
if (!need(8)) return false;
chunkLength = getInt(pos);
chunkKey = getInt(pos+4);
if(chunkLength<0) throw new PNGException("bogus length: "+chunkLength);
if (!need(chunkLength+12)) return false;
chunkCRC = getInt(pos+8+chunkLength);
chunkStart = pos+8;
int calcCRC = crc(inbuf,pos+4,chunkLength+4);
if(chunkCRC!=calcCRC && checkCRC) throw new PNGException("crc corruption");
pos+=chunkLength+12;
return true;
}
private void readAll() throws IOException {
while (getChunk()) handleChunk(chunkKey, inbuf, chunkStart, chunkLength);
}
boolean getData() throws IOException {
while (chunkLength == 0 && getChunk())
if (handleChunk(chunkKey, inbuf, chunkStart, chunkLength))
chunkLength = 0;
return chunkLength > 0;
}
//abstract protected boolean handleChunk(int key, byte[] buf, int st, int len)
// throws IOException;
private static boolean checkCRC = true;
public static boolean getCheckCRC() {
return checkCRC;
}
public static void setCheckCRC(boolean c) {
checkCRC = c;
}
protected void wrc(int c) {
c = c & 0xFF;
if (c <= ' ' || c > 'z') c = '?';
System.out.write(c);
}
protected void wrk(int n) {
wrc(n >> 24);
wrc(n >> 16);
wrc(n >> 8);
wrc(n);
}
public void print() {
wrk(chunkKey);
System.out.print(" " + chunkLength + "\n");
}
/* Table of CRCs of all 8-bit messages. */
private static final int[] crc_table = new int[256];
/* Make the table for a fast CRC. */
static {
for (int n = 0; n < 256; n++) {
int c = n;
for (int k = 0; k < 8; k++)
if ((c & 1) != 0)
c = 0xedb88320 ^ (c >>> 1);
else
c = c >>> 1;
crc_table[n] = c;
}
}
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
static private int update_crc(int crc, byte[] buf, int offset, int len) {
int c = crc;
while (--len >= 0)
c = crc_table[(c ^ buf[offset++]) & 0xff] ^ (c >>> 8);
return c;
}
/* Return the CRC of the bytes buf[0..len-1]. */
static private int crc(byte[] buf, int offset, int len) {
return update_crc(0xffffffff, buf, offset, len) ^ 0xffffffff;
}
public static class Chromaticities {
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) {
whiteX = wx / 100000.0f;
whiteY = wy / 100000.0f;
redX = rx / 100000.0f;
redY = ry / 100000.0f;
greenX = gx / 100000.0f;
greenY = gy / 100000.0f;
blueX = bx / 100000.0f;
blueY = by / 100000.0f;
}
public String toString() {
return "Chromaticities(white=" + whiteX + "," + whiteY + ";red=" +
redX + "," + redY + ";green=" +
greenX + "," + greenY + ";blue=" +
blueX + "," + blueY + ")";
}
}
}
// the following class are added to make it work with ImageDecoder architecture
class PNGFilterInputStream extends FilterInputStream {
PNGImageDecoder owner;
public InputStream underlyingInputStream;
public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) {
super(is);
underlyingInputStream = in;
this.owner = owner;
}
public int available() throws IOException {
return owner.limit - owner.pos + in.available();
}
public boolean markSupported() {
return false;
}
public int read() throws IOException {
if (owner.chunkLength <= 0) if (!owner.getData()) return -1;
owner.chunkLength--;
return owner.inbuf[owner.chunkStart++] & 0xFF;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int st, int len) throws IOException {
if (owner.chunkLength <= 0) if (!owner.getData()) return -1;
if (owner.chunkLength < len) len = owner.chunkLength;
System.arraycopy(owner.inbuf, owner.chunkStart, b, st, len);
owner.chunkLength -= len;
owner.chunkStart += len;
return len;
}
public long skip(long n) throws IOException {
int i;
for (i = 0; i < n && read() >= 0; i++);
return i;
}
}