fixed PNG loader to support indexed color

This commit is contained in:
LAX1DUDE 2022-03-21 01:26:04 -07:00
parent 3e17d57250
commit 94a8c3f6e9
15 changed files with 512 additions and 485 deletions

View File

@ -6,58 +6,57 @@ import com.baislsl.png.util.ByteHandler;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class Chunk { public class Chunk {
protected long length; protected long length;
protected ChunkType type; protected ChunkType type;
protected byte[] data; protected byte[] data;
protected byte[] crc = new byte[4]; protected byte[] crc = new byte[4];
public byte[] dump() { public byte[] dump() {
byte[] output = new byte[4 + 4 + data.length + 4]; byte[] output = new byte[4 + 4 + data.length + 4];
Byte[] lengthBytes = new Byte[4]; Byte[] lengthBytes = new Byte[4];
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
output[3 - i] = (byte) (length & 0xff); output[3 - i] = (byte) (length & 0xff);
} }
String typeName = type.name().toUpperCase(); String typeName = type.name().toUpperCase();
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
output[4 + i] = (byte) typeName.charAt(i); output[4 + i] = (byte) typeName.charAt(i);
} }
System.arraycopy(data, 0, output, 8, data.length); System.arraycopy(data, 0, output, 8, data.length);
System.arraycopy(crc, 0, output, output.length - crc.length, crc.length); System.arraycopy(crc, 0, output, output.length - crc.length, crc.length);
return output; return output;
} }
protected Chunk(byte[] length, byte[] type, byte[] data, byte[] crc) {
this.length = ByteHandler.byteToLong(length);
this.data = data;
this.crc = crc;
protected Chunk(byte[] length, byte[] type, byte[] data, byte[] crc) { for (ChunkType chunkType : ChunkType.values()) {
this.length = ByteHandler.byteToLong(length); if (chunkType.name().equals(ByteHandler.byteToString(type))) {
this.data = data; this.type = chunkType;
this.crc = crc; break;
}
}
for (ChunkType chunkType : ChunkType.values()) { }
if (chunkType.name().equals(ByteHandler.byteToString(type))) {
this.type = chunkType;
break;
}
}
} public long dataLength() {
return data.length;
}
public long dataLength() { public long getLength() {
return data.length; return length;
} }
public long getLength() { public ChunkType getType() {
return length; return type;
} }
public ChunkType getType() { public byte[] getData() {
return type; return data;
} }
public byte[] getData() { public byte[] getCrc() {
return data; return crc;
} }
public byte[] getCrc() {
return crc;
}
} }

View File

@ -8,31 +8,37 @@ import com.baislsl.png.decode.PNG;
*/ */
public enum ChunkType { public enum ChunkType {
IHDR { IHDR {
@Override @Override
public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
png.setIhdr(new IHDR(length, type, data, crc)); png.setIhdr(new IHDR(length, type, data, crc));
} }
}, },
PLTE { tRNS {
@Override @Override
public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
png.setPlte(new PLTE(length, type, data, crc)); png.setTrns(new tRNS(length, type, data, crc));
} }
}, },
IDAT { PLTE {
@Override @Override
public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
png.add(new IDAT(length, type, data, crc)); png.setPlte(new PLTE(length, type, data, crc));
} }
}, },
IEND { IDAT {
@Override @Override
public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
png.setIend(new IEND(length, type, data, crc)); png.add(new IDAT(length, type, data, crc));
} }
}; },
IEND {
@Override
public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
png.setIend(new IEND(length, type, data, crc));
}
};
public abstract void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException; public abstract void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException;
} }

View File

@ -5,7 +5,7 @@ package com.baislsl.png.chunk;
*/ */
public class IDAT extends Chunk { public class IDAT extends Chunk {
public IDAT(byte[] length, byte[] type, byte[] data, byte[] crc) { public IDAT(byte[] length, byte[] type, byte[] data, byte[] crc) {
super(length, type, data, crc); super(length, type, data, crc);
} }
} }

View File

@ -4,7 +4,7 @@ package com.baislsl.png.chunk;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class IEND extends Chunk { public class IEND extends Chunk {
public IEND(byte[] length, byte[] type, byte[] data, byte[] crc) { public IEND(byte[] length, byte[] type, byte[] data, byte[] crc) {
super(length, type, data, crc); super(length, type, data, crc);
} }
} }

View File

@ -7,112 +7,113 @@ import com.baislsl.png.util.ByteHandler;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class IHDR extends Chunk { public class IHDR extends Chunk {
private long width, height; private long width, height;
private int bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod; private int bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod;
final private static int[] colorTypeValid = {0, 2, 3, 4, 6}; final private static int[] colorTypeValid = { 0, 2, 3, 4, 6 };
final private static int[][] mapColorBitDepth = { final private static int[][] mapColorBitDepth = { { 1, 2, 4, 8, 16 }, // color type = 0
{1, 2, 4, 8, 16}, // color type = 0 {}, { 8, 16 }, // color type = 2
{}, { 1, 2, 4, 8 }, // color type = 3
{8, 16}, // color type = 2 { 8, 16 }, // color type = 4
{1, 2, 4, 8}, // color type = 3 {}, { 8, 16 } // color type = 6
{8, 16}, // color type = 4 };
{},
{8, 16} // color type = 6
};
// the number of bytes per complete pixel, rounding up to one // the number of bytes per complete pixel, rounding up to one
public int getBpp() { public int getBpp() {
if (colorType == 2) { // Each pixel is an R,G,B triple. if (colorType == 2) { // Each pixel is an R,G,B triple.
return 3; return 3;
} else if (colorType == 6) { // Each pixel is an R,G,B triple, followed by an alpha sample. } else if (colorType == 6) { // Each pixel is an R,G,B triple, followed by an alpha sample.
return 4; return 4;
} else if (colorType == 3) { // palette index, roll up to 1 } else if (colorType == 3) { // palette index, roll up to 1
return 1; return 1;
} else { } else {
//LOG.error("Error when find bpp"); // LOG.error("Error when find bpp");
return 0; return 0;
} }
} }
public IHDR(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { public IHDR(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
super(length, type, data, crc); super(length, type, data, crc);
build(); build();
checkLegal(); checkLegal();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("width=");sb.append(width); sb.append("width=");
sb.append("height=");sb.append(height); sb.append(width);
sb.append("bitDepth=");sb.append(bitDepth); sb.append("height=");
sb.append("colorType=");sb.append(colorType); sb.append(height);
sb.append("compressionMethod=");sb.append(compressionMethod); sb.append("bitDepth=");
sb.append("filterMethod=");sb.append(filterMethod); sb.append(bitDepth);
sb.append("interlaceMethod=");sb.append(interlaceMethod); sb.append("colorType=");
return sb.toString(); sb.append(colorType);
} sb.append("compressionMethod=");
sb.append(compressionMethod);
sb.append("filterMethod=");
sb.append(filterMethod);
sb.append("interlaceMethod=");
sb.append(interlaceMethod);
return sb.toString();
}
private void build() {
this.width = ByteHandler.byteToLong(data);
this.height = ByteHandler.byteToLong(data, 4);
this.bitDepth = ((int) data[8]) & 0xFF;
this.colorType = ((int) data[9]) & 0xFF;
this.compressionMethod = ((int) data[10]) & 0xFF;
this.filterMethod = ((int) data[11]) & 0xFF;
this.interlaceMethod = ((int) data[12]) & 0xFF;
}
private void build() { private void checkLegal() throws DecodeException {
this.width = ByteHandler.byteToLong(data); boolean legal = false;
this.height = ByteHandler.byteToLong(data, 4); for (int c : colorTypeValid) {
this.bitDepth = ((int)data[8]) & 0xFF; if (c == colorType) {
this.colorType = ((int)data[9]) & 0xFF; legal = true;
this.compressionMethod = ((int)data[10]) & 0xFF; break;
this.filterMethod = ((int)data[11]) & 0xFF; }
this.interlaceMethod = ((int)data[12]) & 0xFF; }
} if (!legal) {
throw new DecodeException("Initialize IHDR : color type not legal to be " + colorType);
}
for (int b : mapColorBitDepth[colorType]) {
if (b == bitDepth) {
return;
}
}
throw new DecodeException(
"Initialzie IHDR : bit depth " + bitDepth + " not valid matching color type " + colorType);
}
public long getWidth() {
return this.width;
}
private void checkLegal() throws DecodeException { public long getHeight() {
boolean legal = false; return this.height;
for (int c : colorTypeValid) { }
if (c == colorType) {
legal = true;
break;
}
}
if (!legal) {
throw new DecodeException("Initialize IHDR : color type not legal to be " + colorType);
}
for (int b : mapColorBitDepth[colorType]) {
if (b == bitDepth) {
return;
}
}
throw new DecodeException("Initialzie IHDR : bit depth " + bitDepth + " not valid matching color type " + colorType);
}
public int getBitDepth() {
return bitDepth;
}
public long getWidth() { public int getColorType() {
return this.width; return colorType;
} }
public long getHeight() { public int getCompressionMethod() {
return this.height; return compressionMethod;
} }
public int getBitDepth() { public int getFilterMethod() {
return bitDepth; return filterMethod;
} }
public int getColorType() {
return colorType;
}
public int getCompressionMethod() {
return compressionMethod;
}
public int getFilterMethod() {
return filterMethod;
}
public int getInterlaceMethod() {
return interlaceMethod;
}
public int getInterlaceMethod() {
return interlaceMethod;
}
} }

View File

@ -2,37 +2,34 @@ package com.baislsl.png.chunk;
import com.baislsl.png.decode.DecodeException; import com.baislsl.png.decode.DecodeException;
/** /**
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class PLTE extends Chunk { public class PLTE extends Chunk {
private int[] color; private int[] color;
public PLTE(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException{ public PLTE(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
super(length, type, data, crc); super(length, type, data, crc);
build(); build();
} }
private void build() throws DecodeException {
if (this.length % 3 != 0)
throw new DecodeException("PLTE length can not be divide by 3");
int size = (int) length / 3;
color = new int[size];
for (int i = 0; i < size; i++) {
color[i] = (((int) data[i * 3]) & 0xFF) << 16 | (((int) data[i * 3 + 1]) & 0xFF) << 8
| (((int) data[i * 3 + 2]) & 0xFF) | 0xFF000000;
}
}
private void build() throws DecodeException{ public int getColor(int i) {
if(this.length % 3 != 0 ) return color[i];
throw new DecodeException("PLTE length can not be divide by 3"); }
int size = (int)length / 3;
color = new int[size];
for(int i=0;i<size;i++){
color[i] = (((int)data[i*3]) & 0xFF) << 16 | (((int)data[i*3 + 1]) & 0xFF) << 8 | (((int)data[i*3 + 2]) & 0xFF);
}
}
public int getColor(int i){
return color[i];
}
public int getPaletteSize(){
return color.length;
}
public int getPaletteSize() {
return color.length;
}
} }

View File

@ -0,0 +1,18 @@
package com.baislsl.png.chunk;
import com.baislsl.png.decode.DecodeException;
/**
* Created by baislsl on 17-7-9.
*/
public class tRNS extends Chunk {
public tRNS(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException {
super(length, type, data, crc);
}
public int getAlpha() {
return (int)data[0] & 0xFF;
}
}

View File

@ -5,17 +5,18 @@ package com.baislsl.png.decode;
*/ */
public class DecodeException extends Exception { public class DecodeException extends Exception {
public DecodeException() {} public DecodeException() {
}
public DecodeException(String message) { public DecodeException(String message) {
super(message); super(message);
} }
public DecodeException(String message, Throwable cause) { public DecodeException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public DecodeException(Throwable cause) { public DecodeException(Throwable cause) {
super(cause); super(cause);
} }
} }

View File

@ -13,88 +13,84 @@ import static com.baislsl.png.util.ByteHandler.byteToLong;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class Decoder { public class Decoder {
//private final static Logger LOG = LoggerFactory.getLogger(Decoder.class); // private final static Logger LOG = LoggerFactory.getLogger(Decoder.class);
private final InputStream in; private final InputStream in;
private final static char[] head = { private final static char[] head = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
};
public Decoder(InputStream in) { public Decoder(InputStream in) {
this.in = in; this.in = in;
} }
private void readHeader() throws DecodeException, IOException { private void readHeader() throws DecodeException, IOException {
byte[] header = readBytes(8); byte[] header = readBytes(8);
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if ((header[i] & 0xff) != (int) head[i]) if ((header[i] & 0xff) != (int) head[i])
throw new DecodeException("It seems that this is not a PNG files"); throw new DecodeException("It seems that this is not a PNG files");
} }
//LOG.info(ByteHandler.byteToString(header)); // LOG.info(ByteHandler.byteToString(header));
} }
private boolean readChunk(PNG png, String chunkName, private boolean readChunk(PNG png, String chunkName, byte[] length, byte[] type, byte[] data, byte[] crc)
byte[] length, byte[] type, throws IOException, DecodeException {
byte[] data, byte[] crc) throws IOException, DecodeException { for (ChunkType chunkType : ChunkType.values()) {
for (ChunkType chunkType : ChunkType.values()) { if (chunkType.name().equalsIgnoreCase(chunkName)) {
if (chunkType.name().equals(chunkName)) { chunkType.apply(png, length, type, data, crc);
chunkType.apply(png, length, type, data, crc); return true;
return true; }
} }
} return false;
return false; }
}
private boolean checkCrc(byte[] data, long crcNumber) { private boolean checkCrc(byte[] data, long crcNumber) {
return crcNumber == CRC.crc(data, data.length); return crcNumber == CRC.crc(data, data.length);
} }
private boolean checkCrc(byte[] type, byte[] data, byte[] crc) { private boolean checkCrc(byte[] type, byte[] data, byte[] crc) {
long crcNumber = byteToLong(crc); long crcNumber = byteToLong(crc);
byte[] crcData = new byte[4 + data.length]; byte[] crcData = new byte[4 + data.length];
System.arraycopy(type, 0, crcData, 0, 4); System.arraycopy(type, 0, crcData, 0, 4);
System.arraycopy(data, 0, crcData, 4, data.length); System.arraycopy(data, 0, crcData, 4, data.length);
return checkCrc(crcData, crcNumber); return checkCrc(crcData, crcNumber);
} }
public PNG readInPNG() throws IOException, DecodeException { public PNG readInPNG() throws IOException, DecodeException {
PNG png = new PNG(); PNG png = new PNG();
readHeader(); readHeader();
String chunkName; String chunkName;
do { do {
byte[] length = readBytes(4); byte[] length = readBytes(4);
long size = byteToLong(length); long size = byteToLong(length);
byte[] type = readBytes(4); byte[] type = readBytes(4);
chunkName = ByteHandler.byteToString(type).toUpperCase(); chunkName = ByteHandler.byteToString(type).toUpperCase();
if("IEND".equals(chunkName)) { if ("IEND".equals(chunkName)) {
break; break;
} }
byte[] data = readBytes((int) size); byte[] data = readBytes((int) size);
byte[] crc = readBytes(4); byte[] crc = readBytes(4);
//LOG.info(ByteHandler.byteToString(type)); // LOG.info(ByteHandler.byteToString(type));
boolean found = readChunk(png, chunkName, length, type, data, crc); boolean found = readChunk(png, chunkName, length, type, data, crc);
if (!found) { if (!found) {
//LOG.info("Not support chunk name {}", chunkName); // LOG.info("Not support chunk name {}", chunkName);
} }
boolean crcMatch = checkCrc(type, data, crc); boolean crcMatch = checkCrc(type, data, crc);
if (!crcMatch) { if (!crcMatch) {
throw new DecodeException("Error data stream for incorrect crc"); throw new DecodeException("Error data stream for incorrect crc");
} }
} while (!"IEND".equals(chunkName)); } while (!"IEND".equals(chunkName));
return png; return png;
} }
private byte[] readBytes(int size) throws IOException {
byte[] result = new byte[size];
int ret = in.read(result, 0, size);
if (ret == -1) return null;
return result;
}
private byte[] readBytes(int size) throws IOException {
byte[] result = new byte[size];
int ret = in.read(result, 0, size);
if (ret == -1)
return null;
return result;
}
} }

View File

@ -6,18 +6,18 @@ import java.util.ArrayList;
public class IDATManager extends ArrayList<IDAT> { public class IDATManager extends ArrayList<IDAT> {
public byte[] getIDATData() { public byte[] getIDATData() {
int dataSize = 0; int dataSize = 0;
for (IDAT idat : this) { for (IDAT idat : this) {
dataSize += idat.dataLength(); dataSize += idat.dataLength();
} }
byte[] data = new byte[dataSize]; byte[] data = new byte[dataSize];
int curPos = 0; int curPos = 0;
for (IDAT idat : this) { for (IDAT idat : this) {
System.arraycopy(idat.getData(), 0, data, curPos, (int) idat.dataLength()); System.arraycopy(idat.getData(), 0, data, curPos, (int) idat.dataLength());
curPos += idat.dataLength(); curPos += idat.dataLength();
} }
return data; return data;
} }
} }

View File

@ -11,112 +11,122 @@ import java.io.IOException;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class PNG { public class PNG {
//private final static Logger LOG = LoggerFactory.getLogger(PNG.class); // private final static Logger LOG = LoggerFactory.getLogger(PNG.class);
public IHDR ihdr; public IHDR ihdr;
public IDATManager idats = new IDATManager(); public IDATManager idats = new IDATManager();
public PLTE plte; public PLTE plte;
public IEND iend; public tRNS trns;
public IEND iend;
public PNG() { public PNG() {
} }
public boolean isAlpha() {
return this.trns != null || ihdr.getBpp() == 4;
}
public PNG(IHDR ihdr, IDATManager idats, PLTE plte, IEND iend) { public int[] getColor() throws DecodeException {
this.ihdr = ihdr; byte[] rawData = idats.getIDATData();
this.idats = idats; byte[] uncompressData = applyLZ77(rawData);
this.plte = plte; byte[][] transferData = applyReverseFilter(uncompressData);
this.iend = iend; int[] colors = applyColorTransfer(transferData);
} return colors;
}
public int[] getColor() throws DecodeException { private int[] applyColorTransfer(byte[][] data) throws DecodeException {
byte[] rawData = idats.getIDATData(); int bpp = ihdr.getBpp();
byte[] uncompressData = applyLZ77(rawData); int width = (int) ihdr.getWidth();
byte[][] transferData = applyReverseFilter(uncompressData); int height = (int) ihdr.getHeight();
int[] colors = applyColorTransfer(transferData); int colorType = ihdr.getColorType();
return colors; int bitDepth = ihdr.getBitDepth();
} int[] colors = new int[width * height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int idx = i * width + j;
switch (colorType) {
case 2:
if (bitDepth == 8) { // bpp = 3
colors[idx] = ((int) data[i][bpp * j] & 0xff) << 16 | ((int) data[i][bpp * j + 1] & 0xff) << 8
| ((int) data[i][bpp * j + 2] & 0xff);
} else {
throw new DecodeException("not supported");
}
break;
case 6:
if (bitDepth == 8) { // bpp = 4
colors[idx] = ((int) data[i][bpp * j] & 0xff) << 16 | ((int) data[i][bpp * j + 1] & 0xff) << 8
| ((int) data[i][bpp * j + 2] & 0xff) | ((int) data[i][bpp * j + 3] & 0xff) << 24;
} else {
throw new DecodeException("not supported");
}
break;
case 3:
int gap = 8 / bitDepth;
int a = (1 << bitDepth) - 1;
int b = gap - (j % gap) - 1;
int pi = (data[i][j / gap] >> (b * bitDepth)) & a;
if (trns != null && trns.getAlpha() == pi) {
colors[idx] = 0;
}else {
colors[idx] = plte.getColor(pi);
}
break;
default:
throw new DecodeException("Do not support color type " + colorType);
}
}
}
private int[] applyColorTransfer(byte[][] data) throws DecodeException { return colors;
int bpp = ihdr.getBpp(); }
int width = (int) ihdr.getWidth();
int height = (int) ihdr.getHeight();
int colorType = ihdr.getColorType();
int bitDepth = ihdr.getBitDepth();
int[] colors = new int[width * height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int idx = i * width + j;
switch (colorType) {
case 2:
if (bitDepth == 8) { // bpp = 3
colors[idx] = ((int)data[i][bpp * j] & 0xff) << 16 | ((int)data[i][bpp * j + 1] & 0xff) << 8 | ((int)data[i][bpp * j + 2] & 0xff);
}else {
throw new DecodeException("not supported");
}
break;
case 6:
if (bitDepth == 8) { // bpp = 4
colors[idx] = ((int)data[i][bpp * j] & 0xff) << 16 | ((int)data[i][bpp * j + 1] & 0xff) << 8 | ((int)data[i][bpp * j + 2] & 0xff) | ((int)data[i][bpp * j + 3] & 0xff) << 24;
}else {
throw new DecodeException("not supported");
}
break;
case 3:
int gap = 8 / bitDepth;
int a = (1 << bitDepth) - 1;
colors[idx] = plte.getColor(data[i][j / gap] & a);
break;
default:
throw new DecodeException("Do not support color type " + colorType);
}
}
}
return colors; private byte[] applyLZ77(byte[] data) throws DecodeException {
} byte[] result;
try {
result = EaglerInflater.uncompress(data);
} catch (IOException e) {
// LOG.error("LZ77 decode error", e);
throw new DecodeException(e);
}
// LOG.info("Size after decode={}", result.length);
return result;
}
private byte[] applyLZ77(byte[] data) throws DecodeException { private byte[][] applyReverseFilter(byte[] data) {
byte[] result; int width = (int) ihdr.getWidth(), height = (int) ihdr.getHeight();
try { return ReverseFilter.apply(data, width, height, ihdr.getBpp());
result = EaglerInflater.uncompress(data); }
} catch (IOException e) {
//LOG.error("LZ77 decode error", e);
throw new DecodeException(e);
}
//LOG.info("Size after decode={}", result.length);
return result;
}
private byte[][] applyReverseFilter(byte[] data) { public void setIdats(IDATManager idats) {
int width = (int) ihdr.getWidth(), height = (int) ihdr.getHeight(); this.idats = idats;
return ReverseFilter.apply(data, width, height, ihdr.getBpp()); }
}
public void setIdats(IDATManager idats) { public void setIhdr(IHDR ihdr) {
this.idats = idats; this.ihdr = ihdr;
} }
public void setIhdr(IHDR ihdr) { public void setPlte(PLTE plte) {
this.ihdr = ihdr; this.plte = plte;
} }
public void setPlte(PLTE plte) { public void setTrns(tRNS trns) {
this.plte = plte; this.trns = trns;
} }
public void setIend(IEND iend) { public void setIend(IEND iend) {
this.iend = iend; this.iend = iend;
} }
public void add(IDAT idat) throws DecodeException { public void add(IDAT idat) throws DecodeException {
idats.add(idat); idats.add(idat);
} }
public long getWidth() { public long getWidth() {
return ihdr.getWidth(); return ihdr.getWidth();
} }
public long getHeight() { public long getHeight() {
return ihdr.getHeight(); return ihdr.getHeight();
} }
} }

View File

@ -5,29 +5,28 @@ package com.baislsl.png.util;
*/ */
public class ByteHandler { public class ByteHandler {
public static long byteToLong(byte[] data, int offset, int size){ public static long byteToLong(byte[] data, int offset, int size) {
long result = 0; long result = 0;
for(int i=0;i<size;i++){ for (int i = 0; i < size; i++) {
result <<= 8; result <<= 8;
result |= ((long)data[offset + i] & 0xff); result |= ((long) data[offset + i] & 0xff);
} }
return result; return result;
} }
public static long byteToLong(byte[] data, int offset){ public static long byteToLong(byte[] data, int offset) {
return byteToLong(data, offset, 4); return byteToLong(data, offset, 4);
} }
public static long byteToLong(byte[] data) { public static long byteToLong(byte[] data) {
return byteToLong(data, 0, 4); return byteToLong(data, 0, 4);
} }
public static String byteToString(byte[] data) {
public static String byteToString(byte[] data) { StringBuilder str = new StringBuilder();
StringBuilder str = new StringBuilder(); for (byte b : data) {
for (byte b : data) { str.append((char) (0x0ff & b));
str.append((char) (0x0ff & b)); }
} return str.toString();
return str.toString(); }
}
} }

View File

@ -4,33 +4,32 @@ package com.baislsl.png.util;
* Created by baislsl on 17-7-9. * Created by baislsl on 17-7-9.
*/ */
public class CRC { public class CRC {
private final static long[] crcTable = new long[256]; private final static long[] crcTable = new long[256];
static { static {
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
long c = i; long c = i;
for (int k = 0; k < 8; k++) { for (int k = 0; k < 8; k++) {
if ((c & 1) != 0) { if ((c & 1) != 0) {
c = 0xedb88320L ^ (c >> 1); c = 0xedb88320L ^ (c >> 1);
} else { } else {
c >>= 1; c >>= 1;
} }
} }
crcTable[i] = c; crcTable[i] = c;
} }
} }
private static long updateCrc(long crc, byte[] buf, int size) {
long ans = crc;
for (int i = 0; i < size; i++) {
ans = crcTable[(int) ((ans ^ buf[i]) & 0xff)] ^ (ans >> 8);
}
return ans;
}
private static long updateCrc(long crc, byte[] buf, int size){ public static long crc(byte[] buf, int size) {
long ans = crc; return updateCrc(0xffffffffL, buf, size) ^ 0xffffffffL;
for(int i=0;i<size;i++){ }
ans = crcTable[(int)((ans^buf[i])&0xff)] ^ (ans >> 8);
}
return ans;
}
public static long crc(byte[] buf, int size){
return updateCrc(0xffffffffL, buf, size) ^ 0xffffffffL;
}
} }

View File

@ -1,63 +1,64 @@
package com.baislsl.png.util; package com.baislsl.png.util;
public class ReverseFilter { public class ReverseFilter {
private ReverseFilter(){} private ReverseFilter() {
}
private static int paethPredictor(int a, int b, int c) {
int p = a + b - c;
int pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
if (pa <= pb && pa <= pc) return a;
if (pb <= pc) return b;
return c;
}
// apply reverse Filter Algorithms to byte data private static int paethPredictor(int a, int b, int c) {
// bpp = 3 int p = a + b - c;
public static byte[][] apply(byte[] data, int width, int height, int bpp) { int pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
int[] filterType = new int[height]; if (pa <= pb && pa <= pc)
int[][] blocks = new int[height][width * bpp]; return a;
int dataIndex = 0; if (pb <= pc)
for (int i = 0; i < height; i++) { return b;
filterType[i] = (int)(data[dataIndex++]) & 0xFF; return c;
for (int j = 0; j < width * bpp; j++) { }
blocks[i][j] = (int)(data[dataIndex++]) & 0xFF;
}
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width * bpp; j++) {
int prior = (i == 0) ? 0 : blocks[i - 1][j];
int rawBpp = (j < bpp) ? 0 : blocks[i][j - bpp];
int bppPrior = (i == 0 || j < bpp) ? 0 : blocks[i - 1][j - bpp];
switch (filterType[i]) {
case 0: // none
break;
case 1: // sub
blocks[i][j] = blocks[i][j] + rawBpp;
break;
case 2: // up
blocks[i][j] = blocks[i][j] + prior;
break;
case 3: //average
blocks[i][j] = blocks[i][j] + (rawBpp + prior) / 2;
break;
case 4: // paeth
blocks[i][j] = blocks[i][j] + paethPredictor(rawBpp, prior, bppPrior);
break;
default:
}
blocks[i][j] &= 0xff;
}
}
byte[][] result = new byte[height][width * bpp]; // apply reverse Filter Algorithms to byte data
for (int i = 0; i < height; i++) { // bpp = 3
for (int j = 0; j < width * bpp; j++) { public static byte[][] apply(byte[] data, int width, int height, int bpp) {
result[i][j] = (byte) blocks[i][j]; int[] filterType = new int[height];
} int[][] blocks = new int[height][width * bpp];
} int dataIndex = 0;
return result; for (int i = 0; i < height; i++) {
} filterType[i] = (int) (data[dataIndex++]) & 0xFF;
for (int j = 0; j < width * bpp; j++) {
blocks[i][j] = (int) (data[dataIndex++]) & 0xFF;
}
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width * bpp; j++) {
int prior = (i == 0) ? 0 : blocks[i - 1][j];
int rawBpp = (j < bpp) ? 0 : blocks[i][j - bpp];
int bppPrior = (i == 0 || j < bpp) ? 0 : blocks[i - 1][j - bpp];
switch (filterType[i]) {
case 0: // none
break;
case 1: // sub
blocks[i][j] = blocks[i][j] + rawBpp;
break;
case 2: // up
blocks[i][j] = blocks[i][j] + prior;
break;
case 3: // average
blocks[i][j] = blocks[i][j] + (rawBpp + prior) / 2;
break;
case 4: // paeth
blocks[i][j] = blocks[i][j] + paethPredictor(rawBpp, prior, bppPrior);
break;
default:
}
blocks[i][j] &= 0xff;
}
}
byte[][] result = new byte[height][width * bpp];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width * bpp; j++) {
result[i][j] = (byte) blocks[i][j];
}
}
return result;
}
} }

View File

@ -39,7 +39,7 @@ public class EaglerImage {
public static final EaglerImage loadImage(byte[] file) { public static final EaglerImage loadImage(byte[] file) {
try { try {
PNG p = (new Decoder(new ByteArrayInputStream(file))).readInPNG(); PNG p = (new Decoder(new ByteArrayInputStream(file))).readInPNG();
return new EaglerImage(p.getColor(), (int)p.getWidth(), (int)p.getHeight(), p.ihdr.getBpp() == 4); return new EaglerImage(p.getColor(), (int)p.getWidth(), (int)p.getHeight(), p.isAlpha());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;