diff --git a/src/main/java/com/baislsl/png/chunk/Chunk.java b/src/main/java/com/baislsl/png/chunk/Chunk.java index eb4f975..1a50dd0 100644 --- a/src/main/java/com/baislsl/png/chunk/Chunk.java +++ b/src/main/java/com/baislsl/png/chunk/Chunk.java @@ -6,58 +6,57 @@ import com.baislsl.png.util.ByteHandler; * Created by baislsl on 17-7-9. */ public class Chunk { - protected long length; - protected ChunkType type; - protected byte[] data; - protected byte[] crc = new byte[4]; + protected long length; + protected ChunkType type; + protected byte[] data; + protected byte[] crc = new byte[4]; - public byte[] dump() { - byte[] output = new byte[4 + 4 + data.length + 4]; - Byte[] lengthBytes = new Byte[4]; - for (int i = 0; i < 4; i++) { - output[3 - i] = (byte) (length & 0xff); - } - String typeName = type.name().toUpperCase(); - for (int i = 0; i < 4; i++) { - output[4 + i] = (byte) typeName.charAt(i); - } - System.arraycopy(data, 0, output, 8, data.length); - System.arraycopy(crc, 0, output, output.length - crc.length, crc.length); - return output; - } + public byte[] dump() { + byte[] output = new byte[4 + 4 + data.length + 4]; + Byte[] lengthBytes = new Byte[4]; + for (int i = 0; i < 4; i++) { + output[3 - i] = (byte) (length & 0xff); + } + String typeName = type.name().toUpperCase(); + for (int i = 0; i < 4; i++) { + output[4 + i] = (byte) typeName.charAt(i); + } + System.arraycopy(data, 0, output, 8, data.length); + System.arraycopy(crc, 0, output, output.length - crc.length, crc.length); + 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) { - this.length = ByteHandler.byteToLong(length); - this.data = data; - this.crc = crc; + for (ChunkType chunkType : ChunkType.values()) { + if (chunkType.name().equals(ByteHandler.byteToString(type))) { + this.type = chunkType; + 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() { - return data.length; - } + public long getLength() { + return length; + } - public long getLength() { - return length; - } + public ChunkType getType() { + return type; + } - public ChunkType getType() { - return type; - } + public byte[] getData() { + return data; + } - public byte[] getData() { - return data; - } - - public byte[] getCrc() { - return crc; - } + public byte[] getCrc() { + return crc; + } } diff --git a/src/main/java/com/baislsl/png/chunk/ChunkType.java b/src/main/java/com/baislsl/png/chunk/ChunkType.java index 330f66a..91a3181 100644 --- a/src/main/java/com/baislsl/png/chunk/ChunkType.java +++ b/src/main/java/com/baislsl/png/chunk/ChunkType.java @@ -8,31 +8,37 @@ import com.baislsl.png.decode.PNG; */ public enum ChunkType { - IHDR { - @Override - public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { - png.setIhdr(new IHDR(length, type, data, crc)); - } - }, - PLTE { - @Override - public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { - png.setPlte(new PLTE(length, type, data, crc)); - } - }, - IDAT { - @Override - public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { - 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)); - } - }; + IHDR { + @Override + public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + png.setIhdr(new IHDR(length, type, data, crc)); + } + }, + tRNS { + @Override + public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + png.setTrns(new tRNS(length, type, data, crc)); + } + }, + PLTE { + @Override + public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + png.setPlte(new PLTE(length, type, data, crc)); + } + }, + IDAT { + @Override + public void apply(PNG png, byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + 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; } diff --git a/src/main/java/com/baislsl/png/chunk/IDAT.java b/src/main/java/com/baislsl/png/chunk/IDAT.java index 91cd845..f8d93d5 100644 --- a/src/main/java/com/baislsl/png/chunk/IDAT.java +++ b/src/main/java/com/baislsl/png/chunk/IDAT.java @@ -5,7 +5,7 @@ package com.baislsl.png.chunk; */ public class IDAT extends Chunk { - public IDAT(byte[] length, byte[] type, byte[] data, byte[] crc) { - super(length, type, data, crc); - } + public IDAT(byte[] length, byte[] type, byte[] data, byte[] crc) { + super(length, type, data, crc); + } } diff --git a/src/main/java/com/baislsl/png/chunk/IEND.java b/src/main/java/com/baislsl/png/chunk/IEND.java index 4f1861e..6132e60 100644 --- a/src/main/java/com/baislsl/png/chunk/IEND.java +++ b/src/main/java/com/baislsl/png/chunk/IEND.java @@ -4,7 +4,7 @@ package com.baislsl.png.chunk; * Created by baislsl on 17-7-9. */ public class IEND extends Chunk { - public IEND(byte[] length, byte[] type, byte[] data, byte[] crc) { - super(length, type, data, crc); - } + public IEND(byte[] length, byte[] type, byte[] data, byte[] crc) { + super(length, type, data, crc); + } } diff --git a/src/main/java/com/baislsl/png/chunk/IHDR.java b/src/main/java/com/baislsl/png/chunk/IHDR.java index 6cb87d3..1c4e7d5 100644 --- a/src/main/java/com/baislsl/png/chunk/IHDR.java +++ b/src/main/java/com/baislsl/png/chunk/IHDR.java @@ -7,112 +7,113 @@ import com.baislsl.png.util.ByteHandler; * Created by baislsl on 17-7-9. */ public class IHDR extends Chunk { - private long width, height; - private int bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod; + private long width, height; + private int bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod; - final private static int[] colorTypeValid = {0, 2, 3, 4, 6}; - final private static int[][] mapColorBitDepth = { - {1, 2, 4, 8, 16}, // color type = 0 - {}, - {8, 16}, // color type = 2 - {1, 2, 4, 8}, // color type = 3 - {8, 16}, // color type = 4 - {}, - {8, 16} // color type = 6 - }; + final private static int[] colorTypeValid = { 0, 2, 3, 4, 6 }; + final private static int[][] mapColorBitDepth = { { 1, 2, 4, 8, 16 }, // color type = 0 + {}, { 8, 16 }, // color type = 2 + { 1, 2, 4, 8 }, // color type = 3 + { 8, 16 }, // color type = 4 + {}, { 8, 16 } // color type = 6 + }; - // the number of bytes per complete pixel, rounding up to one - public int getBpp() { - if (colorType == 2) { // Each pixel is an R,G,B triple. - return 3; - } else if (colorType == 6) { // Each pixel is an R,G,B triple, followed by an alpha sample. - return 4; - } else if (colorType == 3) { // palette index, roll up to 1 - return 1; - } else { - //LOG.error("Error when find bpp"); - return 0; - } - } + // the number of bytes per complete pixel, rounding up to one + public int getBpp() { + if (colorType == 2) { // Each pixel is an R,G,B triple. + return 3; + } else if (colorType == 6) { // Each pixel is an R,G,B triple, followed by an alpha sample. + return 4; + } else if (colorType == 3) { // palette index, roll up to 1 + return 1; + } else { + // LOG.error("Error when find bpp"); + return 0; + } + } - public IHDR(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { - super(length, type, data, crc); - build(); - checkLegal(); - } + public IHDR(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + super(length, type, data, crc); + build(); + checkLegal(); + } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("width=");sb.append(width); - sb.append("height=");sb.append(height); - sb.append("bitDepth=");sb.append(bitDepth); - sb.append("colorType=");sb.append(colorType); - sb.append("compressionMethod=");sb.append(compressionMethod); - sb.append("filterMethod=");sb.append(filterMethod); - sb.append("interlaceMethod=");sb.append(interlaceMethod); - return sb.toString(); - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("width="); + sb.append(width); + sb.append("height="); + sb.append(height); + sb.append("bitDepth="); + sb.append(bitDepth); + sb.append("colorType="); + 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() { - 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 checkLegal() throws DecodeException { + boolean legal = false; + 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 long getWidth() { + return this.width; + } - private void checkLegal() throws DecodeException { - boolean legal = false; - 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 long getHeight() { + return this.height; + } + public int getBitDepth() { + return bitDepth; + } - public long getWidth() { - return this.width; - } + public int getColorType() { + return colorType; + } - public long getHeight() { - return this.height; - } + public int getCompressionMethod() { + return compressionMethod; + } - public int getBitDepth() { - return bitDepth; - } - - public int getColorType() { - return colorType; - } - - public int getCompressionMethod() { - return compressionMethod; - } - - public int getFilterMethod() { - return filterMethod; - } - - public int getInterlaceMethod() { - return interlaceMethod; - } + public int getFilterMethod() { + return filterMethod; + } + public int getInterlaceMethod() { + return interlaceMethod; + } } diff --git a/src/main/java/com/baislsl/png/chunk/PLTE.java b/src/main/java/com/baislsl/png/chunk/PLTE.java index d8271ea..cb6a489 100644 --- a/src/main/java/com/baislsl/png/chunk/PLTE.java +++ b/src/main/java/com/baislsl/png/chunk/PLTE.java @@ -2,37 +2,34 @@ package com.baislsl.png.chunk; import com.baislsl.png.decode.DecodeException; - /** * Created by baislsl on 17-7-9. */ public class PLTE extends Chunk { - private int[] color; + private int[] color; - public PLTE(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException{ - super(length, type, data, crc); - build(); - } + public PLTE(byte[] length, byte[] type, byte[] data, byte[] crc) throws DecodeException { + super(length, type, data, crc); + 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{ - 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 { - public byte[] getIDATData() { - int dataSize = 0; - for (IDAT idat : this) { - dataSize += idat.dataLength(); - } - byte[] data = new byte[dataSize]; - int curPos = 0; - for (IDAT idat : this) { - System.arraycopy(idat.getData(), 0, data, curPos, (int) idat.dataLength()); - curPos += idat.dataLength(); - } - return data; - } + public byte[] getIDATData() { + int dataSize = 0; + for (IDAT idat : this) { + dataSize += idat.dataLength(); + } + byte[] data = new byte[dataSize]; + int curPos = 0; + for (IDAT idat : this) { + System.arraycopy(idat.getData(), 0, data, curPos, (int) idat.dataLength()); + curPos += idat.dataLength(); + } + return data; + } } diff --git a/src/main/java/com/baislsl/png/decode/PNG.java b/src/main/java/com/baislsl/png/decode/PNG.java index b8ecabc..aca2767 100644 --- a/src/main/java/com/baislsl/png/decode/PNG.java +++ b/src/main/java/com/baislsl/png/decode/PNG.java @@ -11,112 +11,122 @@ import java.io.IOException; * Created by baislsl on 17-7-9. */ public class PNG { - //private final static Logger LOG = LoggerFactory.getLogger(PNG.class); - public IHDR ihdr; - public IDATManager idats = new IDATManager(); - public PLTE plte; - public IEND iend; + // private final static Logger LOG = LoggerFactory.getLogger(PNG.class); + public IHDR ihdr; + public IDATManager idats = new IDATManager(); + public PLTE plte; + 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) { - this.ihdr = ihdr; - this.idats = idats; - this.plte = plte; - this.iend = iend; - } + public int[] getColor() throws DecodeException { + byte[] rawData = idats.getIDATData(); + byte[] uncompressData = applyLZ77(rawData); + byte[][] transferData = applyReverseFilter(uncompressData); + int[] colors = applyColorTransfer(transferData); + return colors; + } - public int[] getColor() throws DecodeException { - byte[] rawData = idats.getIDATData(); - byte[] uncompressData = applyLZ77(rawData); - byte[][] transferData = applyReverseFilter(uncompressData); - int[] colors = applyColorTransfer(transferData); - return colors; - } + private int[] applyColorTransfer(byte[][] data) throws DecodeException { + 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; + 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 { - 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; + } - 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 { - 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[][] applyReverseFilter(byte[] data) { + int width = (int) ihdr.getWidth(), height = (int) ihdr.getHeight(); + return ReverseFilter.apply(data, width, height, ihdr.getBpp()); + } - private byte[][] applyReverseFilter(byte[] data) { - int width = (int) ihdr.getWidth(), height = (int) ihdr.getHeight(); - return ReverseFilter.apply(data, width, height, ihdr.getBpp()); - } + public void setIdats(IDATManager idats) { + this.idats = idats; + } - public void setIdats(IDATManager idats) { - this.idats = idats; - } + public void setIhdr(IHDR ihdr) { + this.ihdr = ihdr; + } - public void setIhdr(IHDR ihdr) { - this.ihdr = ihdr; - } + public void setPlte(PLTE plte) { + this.plte = plte; + } - public void setPlte(PLTE plte) { - this.plte = plte; - } + public void setTrns(tRNS trns) { + this.trns = trns; + } - public void setIend(IEND iend) { - this.iend = iend; - } + public void setIend(IEND iend) { + this.iend = iend; + } - public void add(IDAT idat) throws DecodeException { - idats.add(idat); - } + public void add(IDAT idat) throws DecodeException { + idats.add(idat); + } - public long getWidth() { - return ihdr.getWidth(); - } + public long getWidth() { + return ihdr.getWidth(); + } - public long getHeight() { - return ihdr.getHeight(); - } + public long getHeight() { + return ihdr.getHeight(); + } } diff --git a/src/main/java/com/baislsl/png/util/ByteHandler.java b/src/main/java/com/baislsl/png/util/ByteHandler.java index ecc0172..8e07d1d 100644 --- a/src/main/java/com/baislsl/png/util/ByteHandler.java +++ b/src/main/java/com/baislsl/png/util/ByteHandler.java @@ -5,29 +5,28 @@ package com.baislsl.png.util; */ public class ByteHandler { - public static long byteToLong(byte[] data, int offset, int size){ - long result = 0; - for(int i=0;i> 1); - } else { - c >>= 1; - } - } - crcTable[i] = c; - } - } + static { + for (int i = 0; i < 256; i++) { + long c = i; + for (int k = 0; k < 8; k++) { + if ((c & 1) != 0) { + c = 0xedb88320L ^ (c >> 1); + } else { + c >>= 1; + } + } + 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){ - long ans = crc; - for(int i=0;i> 8); - } - return ans; - } - - public static long crc(byte[] buf, int size){ - return updateCrc(0xffffffffL, buf, size) ^ 0xffffffffL; - } + public static long crc(byte[] buf, int size) { + return updateCrc(0xffffffffL, buf, size) ^ 0xffffffffL; + } } diff --git a/src/main/java/com/baislsl/png/util/ReverseFilter.java b/src/main/java/com/baislsl/png/util/ReverseFilter.java index 74fa99d..9016fdc 100644 --- a/src/main/java/com/baislsl/png/util/ReverseFilter.java +++ b/src/main/java/com/baislsl/png/util/ReverseFilter.java @@ -1,63 +1,64 @@ package com.baislsl.png.util; - - public class 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; - } + private ReverseFilter() { + } - // apply reverse Filter Algorithms to byte data - // bpp = 3 - public static byte[][] apply(byte[] data, int width, int height, int bpp) { - int[] filterType = new int[height]; - int[][] blocks = new int[height][width * bpp]; - int dataIndex = 0; - 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; - } - } + 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; + } - 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; - } + // apply reverse Filter Algorithms to byte data + // bpp = 3 + public static byte[][] apply(byte[] data, int width, int height, int bpp) { + int[] filterType = new int[height]; + int[][] blocks = new int[height][width * bpp]; + int dataIndex = 0; + 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; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/EaglerImage.java b/src/main/java/net/lax1dude/eaglercraft/EaglerImage.java index 94a3331..049fdb5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/EaglerImage.java +++ b/src/main/java/net/lax1dude/eaglercraft/EaglerImage.java @@ -39,7 +39,7 @@ public class EaglerImage { public static final EaglerImage loadImage(byte[] file) { try { 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) { e.printStackTrace(); return null;