mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-10 22:01:52 +09:00
added sources for Slick
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
762
lib/slick-source/org/newdawn/slick/opengl/PNGDecoder.java
Normal file
762
lib/slick-source/org/newdawn/slick/opengl/PNGDecoder.java
Normal file
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2010, Matthias Mann
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Matthias Mann nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.newdawn.slick.opengl;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* A PNGDecoder. The slick PNG decoder is based on this class :)
|
||||
*
|
||||
* @author Matthias Mann
|
||||
*/
|
||||
public class PNGDecoder {
|
||||
public static Format ALPHA = new Format(1, true);
|
||||
public static Format LUMINANCE = new Format(1, false);
|
||||
public static Format LUMINANCE_ALPHA = new Format(2, true);
|
||||
public static Format RGB = new Format(3, false);
|
||||
public static Format RGBA = new Format(4, true);
|
||||
public static Format BGRA = new Format(4, true);
|
||||
public static Format ABGR = new Format(4, true);
|
||||
|
||||
public static class Format {
|
||||
|
||||
final int numComponents;
|
||||
final boolean hasAlpha;
|
||||
|
||||
private Format(int numComponents, boolean hasAlpha) {
|
||||
this.numComponents = numComponents;
|
||||
this.hasAlpha = hasAlpha;
|
||||
}
|
||||
|
||||
public int getNumComponents() {
|
||||
return numComponents;
|
||||
}
|
||||
|
||||
public boolean isHasAlpha() {
|
||||
return hasAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] SIGNATURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10};
|
||||
|
||||
private static final int IHDR = 0x49484452;
|
||||
private static final int PLTE = 0x504C5445;
|
||||
private static final int tRNS = 0x74524E53;
|
||||
private static final int IDAT = 0x49444154;
|
||||
private static final int IEND = 0x49454E44;
|
||||
|
||||
private static final byte COLOR_GREYSCALE = 0;
|
||||
private static final byte COLOR_TRUECOLOR = 2;
|
||||
private static final byte COLOR_INDEXED = 3;
|
||||
private static final byte COLOR_GREYALPHA = 4;
|
||||
private static final byte COLOR_TRUEALPHA = 6;
|
||||
|
||||
private final InputStream input;
|
||||
private final CRC32 crc;
|
||||
private final byte[] buffer;
|
||||
|
||||
private int chunkLength;
|
||||
private int chunkType;
|
||||
private int chunkRemaining;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private int bitdepth;
|
||||
private int colorType;
|
||||
private int bytesPerPixel;
|
||||
private byte[] palette;
|
||||
private byte[] paletteA;
|
||||
private byte[] transPixel;
|
||||
|
||||
public PNGDecoder(InputStream input) throws IOException {
|
||||
this.input = input;
|
||||
this.crc = new CRC32();
|
||||
this.buffer = new byte[4096];
|
||||
|
||||
readFully(buffer, 0, SIGNATURE.length);
|
||||
if(!checkSignature(buffer)) {
|
||||
throw new IOException("Not a valid PNG file");
|
||||
}
|
||||
|
||||
openChunk(IHDR);
|
||||
readIHDR();
|
||||
closeChunk();
|
||||
|
||||
searchIDAT: for(;;) {
|
||||
openChunk();
|
||||
switch (chunkType) {
|
||||
case IDAT:
|
||||
break searchIDAT;
|
||||
case PLTE:
|
||||
readPLTE();
|
||||
break;
|
||||
case tRNS:
|
||||
readtRNS();
|
||||
break;
|
||||
}
|
||||
closeChunk();
|
||||
}
|
||||
|
||||
if(colorType == COLOR_INDEXED && palette == null) {
|
||||
throw new IOException("Missing PLTE chunk");
|
||||
}
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public boolean hasAlpha() {
|
||||
return colorType == COLOR_TRUEALPHA ||
|
||||
paletteA != null || transPixel != null;
|
||||
}
|
||||
|
||||
public boolean isRGB() {
|
||||
return colorType == COLOR_TRUEALPHA ||
|
||||
colorType == COLOR_TRUECOLOR ||
|
||||
colorType == COLOR_INDEXED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the implemented format conversion for the desired format.
|
||||
*
|
||||
* @param fmt the desired format
|
||||
* @return format which best matches the desired format
|
||||
* @throws UnsupportedOperationException if this PNG file can't be decoded
|
||||
*/
|
||||
public Format decideTextureFormat(Format fmt) {
|
||||
switch (colorType) {
|
||||
case COLOR_TRUECOLOR:
|
||||
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA) || (fmt == RGB)) {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
return RGB;
|
||||
case COLOR_TRUEALPHA:
|
||||
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA) || (fmt == RGB)) {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
return RGBA;
|
||||
case COLOR_GREYSCALE:
|
||||
if ((fmt == LUMINANCE) || (fmt == ALPHA)) {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
return LUMINANCE;
|
||||
case COLOR_GREYALPHA:
|
||||
return LUMINANCE_ALPHA;
|
||||
case COLOR_INDEXED:
|
||||
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA)) {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
return RGBA;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public void decode(ByteBuffer buffer, int stride, Format fmt) throws IOException {
|
||||
final int offset = buffer.position();
|
||||
final int lineSize = ((width * bitdepth + 7) / 8) * bytesPerPixel;
|
||||
byte[] curLine = new byte[lineSize+1];
|
||||
byte[] prevLine = new byte[lineSize+1];
|
||||
byte[] palLine = (bitdepth < 8) ? new byte[width+1] : null;
|
||||
|
||||
final Inflater inflater = new Inflater();
|
||||
try {
|
||||
for(int y=0 ; y<height ; y++) {
|
||||
readChunkUnzip(inflater, curLine, 0, curLine.length);
|
||||
unfilter(curLine, prevLine);
|
||||
|
||||
buffer.position(offset + y*stride);
|
||||
|
||||
switch (colorType) {
|
||||
case COLOR_TRUECOLOR:
|
||||
if (fmt == ABGR) {
|
||||
copyRGBtoABGR(buffer, curLine);
|
||||
}
|
||||
else if (fmt == RGBA) {
|
||||
copyRGBtoRGBA(buffer, curLine);
|
||||
}
|
||||
else if (fmt == BGRA) {
|
||||
copyRGBtoBGRA(buffer, curLine);
|
||||
}
|
||||
else if (fmt == RGB) {
|
||||
copy(buffer, curLine);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported format for this image");
|
||||
}
|
||||
break;
|
||||
case COLOR_TRUEALPHA:
|
||||
if (fmt == ABGR) {
|
||||
copyRGBAtoABGR(buffer, curLine);
|
||||
} else if (fmt == RGBA) {
|
||||
copy(buffer, curLine);
|
||||
} else if (fmt == BGRA) {
|
||||
copyRGBAtoBGRA(buffer, curLine); break;
|
||||
} else if (fmt == RGB) {
|
||||
copyRGBAtoRGB(buffer, curLine); break;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported format for this image");
|
||||
}
|
||||
break;
|
||||
case COLOR_GREYSCALE:
|
||||
if ((fmt == LUMINANCE) || (fmt == ALPHA)) {
|
||||
copy(buffer, curLine);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported format for this image");
|
||||
}
|
||||
break;
|
||||
case COLOR_GREYALPHA:
|
||||
if (fmt == LUMINANCE_ALPHA) {
|
||||
copy(buffer, curLine);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported format for this image");
|
||||
}
|
||||
break;
|
||||
case COLOR_INDEXED:
|
||||
switch(bitdepth) {
|
||||
case 8: palLine = curLine; break;
|
||||
case 4: expand4(curLine, palLine); break;
|
||||
case 2: expand2(curLine, palLine); break;
|
||||
case 1: expand1(curLine, palLine); break;
|
||||
default: throw new UnsupportedOperationException("Unsupported bitdepth for this image");
|
||||
}
|
||||
if (fmt == ABGR) {
|
||||
copyPALtoABGR(buffer, palLine);
|
||||
} else if (fmt == RGBA) {
|
||||
copyPALtoRGBA(buffer, palLine);
|
||||
} else if (fmt == BGRA) {
|
||||
copyPALtoBGRA(buffer, palLine);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported format for this image");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
byte[] tmp = curLine;
|
||||
curLine = prevLine;
|
||||
prevLine = tmp;
|
||||
}
|
||||
} finally {
|
||||
inflater.end();
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(ByteBuffer buffer, byte[] curLine) {
|
||||
buffer.put(curLine, 1, curLine.length-1);
|
||||
}
|
||||
|
||||
private void copyRGBtoABGR(ByteBuffer buffer, byte[] curLine) {
|
||||
if(transPixel != null) {
|
||||
byte tr = transPixel[1];
|
||||
byte tg = transPixel[3];
|
||||
byte tb = transPixel[5];
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
byte r = curLine[i];
|
||||
byte g = curLine[i+1];
|
||||
byte b = curLine[i+2];
|
||||
byte a = (byte)0xFF;
|
||||
if(r==tr && g==tg && b==tb) {
|
||||
a = 0;
|
||||
}
|
||||
buffer.put(a).put(b).put(g).put(r);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
buffer.put((byte)0xFF).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRGBtoRGBA(ByteBuffer buffer, byte[] curLine) {
|
||||
if(transPixel != null) {
|
||||
byte tr = transPixel[1];
|
||||
byte tg = transPixel[3];
|
||||
byte tb = transPixel[5];
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
byte r = curLine[i];
|
||||
byte g = curLine[i+1];
|
||||
byte b = curLine[i+2];
|
||||
byte a = (byte)0xFF;
|
||||
if(r==tr && g==tg && b==tb) {
|
||||
a = 0;
|
||||
}
|
||||
buffer.put(r).put(g).put(b).put(a);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]).put((byte)0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRGBtoBGRA(ByteBuffer buffer, byte[] curLine) {
|
||||
if(transPixel != null) {
|
||||
byte tr = transPixel[1];
|
||||
byte tg = transPixel[3];
|
||||
byte tb = transPixel[5];
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
byte r = curLine[i];
|
||||
byte g = curLine[i+1];
|
||||
byte b = curLine[i+2];
|
||||
byte a = (byte)0xFF;
|
||||
if(r==tr && g==tg && b==tb) {
|
||||
a = 0;
|
||||
}
|
||||
buffer.put(b).put(g).put(r).put(a);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=3) {
|
||||
buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put((byte)0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRGBAtoABGR(ByteBuffer buffer, byte[] curLine) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=4) {
|
||||
buffer.put(curLine[i+3]).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRGBAtoBGRA(ByteBuffer buffer, byte[] curLine) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=4) {
|
||||
buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i+0]).put(curLine[i+3]);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRGBAtoRGB(ByteBuffer buffer, byte[] curLine) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=4) {
|
||||
buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPALtoABGR(ByteBuffer buffer, byte[] curLine) {
|
||||
if(paletteA != null) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = paletteA[idx];
|
||||
buffer.put(a).put(b).put(g).put(r);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = (byte)0xFF;
|
||||
buffer.put(a).put(b).put(g).put(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPALtoRGBA(ByteBuffer buffer, byte[] curLine) {
|
||||
if(paletteA != null) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = paletteA[idx];
|
||||
buffer.put(r).put(g).put(b).put(a);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = (byte)0xFF;
|
||||
buffer.put(r).put(g).put(b).put(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPALtoBGRA(ByteBuffer buffer, byte[] curLine) {
|
||||
if(paletteA != null) {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = paletteA[idx];
|
||||
buffer.put(b).put(g).put(r).put(a);
|
||||
}
|
||||
} else {
|
||||
for(int i=1,n=curLine.length ; i<n ; i+=1) {
|
||||
int idx = curLine[i] & 255;
|
||||
byte r = palette[idx*3 + 0];
|
||||
byte g = palette[idx*3 + 1];
|
||||
byte b = palette[idx*3 + 2];
|
||||
byte a = (byte)0xFF;
|
||||
buffer.put(b).put(g).put(r).put(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void expand4(byte[] src, byte[] dst) {
|
||||
for(int i=1,n=dst.length ; i<n ; i+=2) {
|
||||
int val = src[1 + (i >> 1)] & 255;
|
||||
switch(n-i) {
|
||||
default: dst[i+1] = (byte)(val & 15);
|
||||
case 1: dst[i ] = (byte)(val >> 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void expand2(byte[] src, byte[] dst) {
|
||||
for(int i=1,n=dst.length ; i<n ; i+=4) {
|
||||
int val = src[1 + (i >> 2)] & 255;
|
||||
switch(n-i) {
|
||||
default: dst[i+3] = (byte)((val ) & 3);
|
||||
case 3: dst[i+2] = (byte)((val >> 2) & 3);
|
||||
case 2: dst[i+1] = (byte)((val >> 4) & 3);
|
||||
case 1: dst[i ] = (byte)((val >> 6) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void expand1(byte[] src, byte[] dst) {
|
||||
for(int i=1,n=dst.length ; i<n ; i+=8) {
|
||||
int val = src[1 + (i >> 3)] & 255;
|
||||
switch(n-i) {
|
||||
default: dst[i+7] = (byte)((val ) & 1);
|
||||
case 7: dst[i+6] = (byte)((val >> 1) & 1);
|
||||
case 6: dst[i+5] = (byte)((val >> 2) & 1);
|
||||
case 5: dst[i+4] = (byte)((val >> 3) & 1);
|
||||
case 4: dst[i+3] = (byte)((val >> 4) & 1);
|
||||
case 3: dst[i+2] = (byte)((val >> 5) & 1);
|
||||
case 2: dst[i+1] = (byte)((val >> 6) & 1);
|
||||
case 1: dst[i ] = (byte)((val >> 7) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unfilter(byte[] curLine, byte[] prevLine) throws IOException {
|
||||
switch (curLine[0]) {
|
||||
case 0: // none
|
||||
break;
|
||||
case 1:
|
||||
unfilterSub(curLine);
|
||||
break;
|
||||
case 2:
|
||||
unfilterUp(curLine, prevLine);
|
||||
break;
|
||||
case 3:
|
||||
unfilterAverage(curLine, prevLine);
|
||||
break;
|
||||
case 4:
|
||||
unfilterPaeth(curLine, prevLine);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("invalide filter type in scanline: " + curLine[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void unfilterSub(byte[] curLine) {
|
||||
final int bpp = this.bytesPerPixel;
|
||||
for(int i=bpp+1,n=curLine.length ; i<n ; ++i) {
|
||||
curLine[i] += curLine[i-bpp];
|
||||
}
|
||||
}
|
||||
|
||||
private void unfilterUp(byte[] curLine, byte[] prevLine) {
|
||||
final int bpp = this.bytesPerPixel;
|
||||
for(int i=1,n=curLine.length ; i<n ; ++i) {
|
||||
curLine[i] += prevLine[i];
|
||||
}
|
||||
}
|
||||
|
||||
private void unfilterAverage(byte[] curLine, byte[] prevLine) {
|
||||
final int bpp = this.bytesPerPixel;
|
||||
|
||||
int i;
|
||||
for(i=1 ; i<=bpp ; ++i) {
|
||||
curLine[i] += (byte)((prevLine[i] & 0xFF) >>> 1);
|
||||
}
|
||||
for(int n=curLine.length ; i<n ; ++i) {
|
||||
curLine[i] += (byte)(((prevLine[i] & 0xFF) + (curLine[i - bpp] & 0xFF)) >>> 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void unfilterPaeth(byte[] curLine, byte[] prevLine) {
|
||||
final int bpp = this.bytesPerPixel;
|
||||
|
||||
int i;
|
||||
for(i=1 ; i<=bpp ; ++i) {
|
||||
curLine[i] += prevLine[i];
|
||||
}
|
||||
for(int n=curLine.length ; i<n ; ++i) {
|
||||
int a = curLine[i - bpp] & 255;
|
||||
int b = prevLine[i] & 255;
|
||||
int c = prevLine[i - bpp] & 255;
|
||||
int p = a + b - c;
|
||||
int pa = p - a; if(pa < 0) pa = -pa;
|
||||
int pb = p - b; if(pb < 0) pb = -pb;
|
||||
int pc = p - c; if(pc < 0) pc = -pc;
|
||||
if(pa<=pb && pa<=pc)
|
||||
c = a;
|
||||
else if(pb<=pc)
|
||||
c = b;
|
||||
curLine[i] += (byte)c;
|
||||
}
|
||||
}
|
||||
|
||||
private void readIHDR() throws IOException {
|
||||
checkChunkLength(13);
|
||||
readChunk(buffer, 0, 13);
|
||||
width = readInt(buffer, 0);
|
||||
height = readInt(buffer, 4);
|
||||
bitdepth = buffer[8] & 255;
|
||||
colorType = buffer[9] & 255;
|
||||
|
||||
switch (colorType) {
|
||||
case COLOR_GREYSCALE:
|
||||
if(bitdepth != 8) {
|
||||
throw new IOException("Unsupported bit depth: " + bitdepth);
|
||||
}
|
||||
bytesPerPixel = 1;
|
||||
break;
|
||||
case COLOR_GREYALPHA:
|
||||
if(bitdepth != 8) {
|
||||
throw new IOException("Unsupported bit depth: " + bitdepth);
|
||||
}
|
||||
bytesPerPixel = 2;
|
||||
break;
|
||||
case COLOR_TRUECOLOR:
|
||||
if(bitdepth != 8) {
|
||||
throw new IOException("Unsupported bit depth: " + bitdepth);
|
||||
}
|
||||
bytesPerPixel = 3;
|
||||
break;
|
||||
case COLOR_TRUEALPHA:
|
||||
if(bitdepth != 8) {
|
||||
throw new IOException("Unsupported bit depth: " + bitdepth);
|
||||
}
|
||||
bytesPerPixel = 4;
|
||||
break;
|
||||
case COLOR_INDEXED:
|
||||
switch(bitdepth) {
|
||||
case 8:
|
||||
case 4:
|
||||
case 2:
|
||||
case 1:
|
||||
bytesPerPixel = 1;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unsupported bit depth: " + bitdepth);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unsupported color format: " + colorType);
|
||||
}
|
||||
|
||||
if(buffer[10] != 0) {
|
||||
throw new IOException("unsupported compression method");
|
||||
}
|
||||
if(buffer[11] != 0) {
|
||||
throw new IOException("unsupported filtering method");
|
||||
}
|
||||
if(buffer[12] != 0) {
|
||||
throw new IOException("unsupported interlace method");
|
||||
}
|
||||
}
|
||||
|
||||
private void readPLTE() throws IOException {
|
||||
int paletteEntries = chunkLength / 3;
|
||||
if(paletteEntries < 1 || paletteEntries > 256 || (chunkLength % 3) != 0) {
|
||||
throw new IOException("PLTE chunk has wrong length");
|
||||
}
|
||||
palette = new byte[paletteEntries*3];
|
||||
readChunk(palette, 0, palette.length);
|
||||
}
|
||||
|
||||
private void readtRNS() throws IOException {
|
||||
switch (colorType) {
|
||||
case COLOR_GREYSCALE:
|
||||
checkChunkLength(2);
|
||||
transPixel = new byte[2];
|
||||
readChunk(transPixel, 0, 2);
|
||||
break;
|
||||
case COLOR_TRUECOLOR:
|
||||
checkChunkLength(6);
|
||||
transPixel = new byte[6];
|
||||
readChunk(transPixel, 0, 6);
|
||||
break;
|
||||
case COLOR_INDEXED:
|
||||
if(palette == null) {
|
||||
throw new IOException("tRNS chunk without PLTE chunk");
|
||||
}
|
||||
paletteA = new byte[palette.length/3];
|
||||
Arrays.fill(paletteA, (byte)0xFF);
|
||||
readChunk(paletteA, 0, paletteA.length);
|
||||
break;
|
||||
default:
|
||||
// just ignore it
|
||||
}
|
||||
}
|
||||
|
||||
private void closeChunk() throws IOException {
|
||||
if(chunkRemaining > 0) {
|
||||
// just skip the rest and the CRC
|
||||
skip(chunkRemaining + 4);
|
||||
} else {
|
||||
readFully(buffer, 0, 4);
|
||||
int expectedCrc = readInt(buffer, 0);
|
||||
int computedCrc = (int)crc.getValue();
|
||||
if(computedCrc != expectedCrc) {
|
||||
throw new IOException("Invalid CRC");
|
||||
}
|
||||
}
|
||||
chunkRemaining = 0;
|
||||
chunkLength = 0;
|
||||
chunkType = 0;
|
||||
}
|
||||
|
||||
private void openChunk() throws IOException {
|
||||
readFully(buffer, 0, 8);
|
||||
chunkLength = readInt(buffer, 0);
|
||||
chunkType = readInt(buffer, 4);
|
||||
chunkRemaining = chunkLength;
|
||||
crc.reset();
|
||||
crc.update(buffer, 4, 4); // only chunkType
|
||||
}
|
||||
|
||||
private void openChunk(int expected) throws IOException {
|
||||
openChunk();
|
||||
if(chunkType != expected) {
|
||||
throw new IOException("Expected chunk: " + Integer.toHexString(expected));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkChunkLength(int expected) throws IOException {
|
||||
if(chunkLength != expected) {
|
||||
throw new IOException("Chunk has wrong size");
|
||||
}
|
||||
}
|
||||
|
||||
private int readChunk(byte[] buffer, int offset, int length) throws IOException {
|
||||
if(length > chunkRemaining) {
|
||||
length = chunkRemaining;
|
||||
}
|
||||
readFully(buffer, offset, length);
|
||||
crc.update(buffer, offset, length);
|
||||
chunkRemaining -= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
private void refillInflater(Inflater inflater) throws IOException {
|
||||
while(chunkRemaining == 0) {
|
||||
closeChunk();
|
||||
openChunk(IDAT);
|
||||
}
|
||||
int read = readChunk(buffer, 0, buffer.length);
|
||||
inflater.setInput(buffer, 0, read);
|
||||
}
|
||||
|
||||
private void readChunkUnzip(Inflater inflater, byte[] buffer, int offset, int length) throws IOException {
|
||||
try {
|
||||
do {
|
||||
int read = inflater.inflate(buffer, offset, length);
|
||||
if(read <= 0) {
|
||||
if(inflater.finished()) {
|
||||
throw new EOFException();
|
||||
}
|
||||
if(inflater.needsInput()) {
|
||||
refillInflater(inflater);
|
||||
} else {
|
||||
throw new IOException("Can't inflate " + length + " bytes");
|
||||
}
|
||||
} else {
|
||||
offset += read;
|
||||
length -= read;
|
||||
}
|
||||
} while(length > 0);
|
||||
} catch (DataFormatException ex) {
|
||||
throw (IOException)(new IOException("inflate error").initCause(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private void readFully(byte[] buffer, int offset, int length) throws IOException {
|
||||
do {
|
||||
int read = input.read(buffer, offset, length);
|
||||
if(read < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
offset += read;
|
||||
length -= read;
|
||||
} while(length > 0);
|
||||
}
|
||||
|
||||
private int readInt(byte[] buffer, int offset) {
|
||||
return
|
||||
((buffer[offset ] ) << 24) |
|
||||
((buffer[offset+1] & 255) << 16) |
|
||||
((buffer[offset+2] & 255) << 8) |
|
||||
((buffer[offset+3] & 255) );
|
||||
}
|
||||
|
||||
private void skip(long amount) throws IOException {
|
||||
while(amount > 0) {
|
||||
long skipped = input.skip(amount);
|
||||
if(skipped < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
amount -= skipped;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkSignature(byte[] buffer) {
|
||||
for(int i=0 ; i<SIGNATURE.length ; i++) {
|
||||
if(buffer[i] != SIGNATURE[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user