|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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=http://www.ietf.org/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; |
|
private java.util.Hashtable properties; |
|
|
|
|
|
*/ |
|
private ColorModel cm; |
|
private byte[] red_map, green_map, blue_map, alpha_map; |
|
private int transparentPixel = -1; |
|
private byte[] transparentPixel_16 = null; |
|
private static ColorModel greyModels[] = new ColorModel[4]; |
|
/* this is not needed |
|
PNGImageDecoder next; |
|
*/ |
|
|
|
private void property(String key,Object value) { |
|
if(value==null) return; |
|
if(properties==null) properties=new java.util.Hashtable(); |
|
properties.put(key,value); |
|
} |
|
private void property(String key,float value) { |
|
property(key,new Float(value)); |
|
} |
|
private final void pngassert(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: |
|
pngassert(len==6); |
|
c = new Color(buf[st]&0xff,buf[st+2]&0xff,buf[st+4]&0xff); |
|
break; |
|
case COLOR|PALETTE: |
|
case COLOR|PALETTE|ALPHA: |
|
pngassert(len==1); |
|
int ix = buf[st]&0xFF; |
|
pngassert(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: |
|
pngassert(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); |
|
|
|
|
|
*/ |
|
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: |
|
case COLOR|ALPHA: |
|
pngassert(len==6); |
|
if (bitDepth == 16) { |
|
transparentPixel_16 = new byte[6]; |
|
for (int i = 0; i < 6; i++) { |
|
transparentPixel_16[i] = (byte)getByte(st + i); |
|
} |
|
} else { |
|
transparentPixel = |
|
((getShort(st + 0)&0xFF)<<16) |
|
| ((getShort(st + 2)&0xFF)<< 8) |
|
| ((getShort(st + 4)&0xFF) ); |
|
} |
|
break; |
|
case GRAY: |
|
case GRAY|ALPHA: |
|
pngassert(len==2); |
|
|
|
|
|
|
|
*/ |
|
int t = getShort(st); |
|
t = 0xFF & ((bitDepth == 16) ? (t >> 8) : t); |
|
transparentPixel = (t<<16) | (t<< 8) | t; |
|
break; |
|
} |
|
break; |
|
case zTXtChunk: break; |
|
} |
|
return true; |
|
} |
|
public class PNGException extends IOException { |
|
PNGException(String s) { super(s); } |
|
} |
|
|
|
|
|
*/ |
|
public void produceImage() throws IOException, ImageFormatException { |
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
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)); |
|
|
|
if (transparentPixel == -1) { |
|
cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp); |
|
} else { |
|
cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp, |
|
(transparentPixel & 0xFF)); |
|
} |
|
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); |
|
*/ |
|
|
|
setDimensions(width, height); |
|
setColorModel(cm); |
|
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. |
|
|
|
|
|
*/ |
|
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 pixelBufferInc = interlaceMethod==0 ? rowInc*width : 0; |
|
int rowOffset = rowStride*row; |
|
boolean firstRow = true; |
|
|
|
byte[] rowByteBuffer = new byte[rowByteWidth]; |
|
byte[] prevRowByteBuffer = new byte[rowByteWidth]; |
|
|
|
|
|
*/ |
|
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; |
|
int pixel = 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): |
|
pixel = |
|
((rowByteBuffer[spos ]&0xFF)<<16) |
|
| ((rowByteBuffer[spos+1]&0xFF)<< 8) |
|
| ((rowByteBuffer[spos+2]&0xFF) ); |
|
if (pixel != transparentPixel) { |
|
pixel |= 0xff000000; |
|
} |
|
wPixels[col+rowOffset] = pixel; |
|
spos+=3; |
|
break; |
|
case COLOR|(16<<3): |
|
pixel = |
|
((rowByteBuffer[spos ]&0xFF)<<16) |
|
| ((rowByteBuffer[spos+2]&0xFF)<< 8) |
|
| ((rowByteBuffer[spos+4]&0xFF) ); |
|
|
|
boolean isTransparent = (transparentPixel_16 != null); |
|
for (int i = 0; isTransparent && (i < 6); i++) { |
|
isTransparent &= |
|
(rowByteBuffer[spos + i] & 0xFF) == (transparentPixel_16[i] & 0xFF); |
|
} |
|
if (!isTransparent) { |
|
pixel |= 0xff000000; |
|
} |
|
wPixels[col+rowOffset] = pixel; |
|
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))&15); |
|
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"); |
|
} |
|
|
|
|
|
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); |
|
*/ |
|
|
|
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); |
|
*/ |
|
|
|
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); |
|
*/ |
|
|
|
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); |
|
*/ |
|
|
|
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(IOException e) { |
|
if(!aborted) { |
|
|
|
|
|
|
|
*/ |
|
property("error", e); |
|
|
|
|
|
|
|
*/ |
|
imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE, true); |
|
throw e; |
|
} |
|
} 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, rval; |
|
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 }; |
|
|
|
|
|
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; |
|
|
|
|
|
|
|
*/ |
|
public PNGImageDecoder(InputStreamImageSource src, InputStream input) throws IOException { |
|
|
|
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(); |
|
*/ |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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) |
|
|
|
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"); |
|
} |
|
|
|
|
|
private static final int[] crc_table = new int[256]; |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
} |