fixed PNG loader to support indexed color
This commit is contained in:
parent
3e17d57250
commit
94a8c3f6e9
|
@ -26,7 +26,6 @@ public class Chunk {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Chunk(byte[] length, byte[] type, byte[] data, byte[] crc) {
|
protected Chunk(byte[] length, byte[] type, byte[] data, byte[] crc) {
|
||||||
this.length = ByteHandler.byteToLong(length);
|
this.length = ByteHandler.byteToLong(length);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
|
@ -14,6 +14,12 @@ public enum ChunkType {
|
||||||
png.setIhdr(new IHDR(length, type, data, crc));
|
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 {
|
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 {
|
||||||
|
|
|
@ -11,14 +11,11 @@ public class IHDR extends Chunk {
|
||||||
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
|
||||||
{},
|
|
||||||
{8, 16}, // color type = 2
|
|
||||||
{ 1, 2, 4, 8 }, // color type = 3
|
{ 1, 2, 4, 8 }, // color type = 3
|
||||||
{ 8, 16 }, // color type = 4
|
{ 8, 16 }, // color type = 4
|
||||||
{},
|
{}, { 8, 16 } // color type = 6
|
||||||
{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
|
||||||
|
@ -44,17 +41,23 @@ public class IHDR extends Chunk {
|
||||||
@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=");
|
||||||
|
sb.append(colorType);
|
||||||
|
sb.append("compressionMethod=");
|
||||||
|
sb.append(compressionMethod);
|
||||||
|
sb.append("filterMethod=");
|
||||||
|
sb.append(filterMethod);
|
||||||
|
sb.append("interlaceMethod=");
|
||||||
|
sb.append(interlaceMethod);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void build() {
|
private void build() {
|
||||||
this.width = ByteHandler.byteToLong(data);
|
this.width = ByteHandler.byteToLong(data);
|
||||||
this.height = ByteHandler.byteToLong(data, 4);
|
this.height = ByteHandler.byteToLong(data, 4);
|
||||||
|
@ -65,7 +68,6 @@ public class IHDR extends Chunk {
|
||||||
this.interlaceMethod = ((int) data[12]) & 0xFF;
|
this.interlaceMethod = ((int) data[12]) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void checkLegal() throws DecodeException {
|
private void checkLegal() throws DecodeException {
|
||||||
boolean legal = false;
|
boolean legal = false;
|
||||||
for (int c : colorTypeValid) {
|
for (int c : colorTypeValid) {
|
||||||
|
@ -82,10 +84,10 @@ public class IHDR extends Chunk {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new DecodeException("Initialzie IHDR : bit depth " + bitDepth + " not valid matching color type " + colorType);
|
throw new DecodeException(
|
||||||
|
"Initialzie IHDR : bit depth " + bitDepth + " not valid matching color type " + colorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getWidth() {
|
public long getWidth() {
|
||||||
return this.width;
|
return this.width;
|
||||||
}
|
}
|
||||||
|
@ -114,5 +116,4 @@ public class IHDR extends Chunk {
|
||||||
return interlaceMethod;
|
return interlaceMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
@ -14,15 +13,14 @@ public class PLTE extends Chunk {
|
||||||
build();
|
build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void build() throws DecodeException {
|
private void build() throws DecodeException {
|
||||||
if (this.length % 3 != 0)
|
if (this.length % 3 != 0)
|
||||||
throw new DecodeException("PLTE length can not be divide by 3");
|
throw new DecodeException("PLTE length can not be divide by 3");
|
||||||
|
|
||||||
int size = (int) length / 3;
|
int size = (int) length / 3;
|
||||||
color = new int[size];
|
color = new int[size];
|
||||||
for (int i = 0; i < size; i++) {
|
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);
|
color[i] = (((int) data[i * 3]) & 0xFF) << 16 | (((int) data[i * 3 + 1]) & 0xFF) << 8
|
||||||
|
| (((int) data[i * 3 + 2]) & 0xFF) | 0xFF000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,5 +32,4 @@ public class PLTE extends Chunk {
|
||||||
return color.length;
|
return color.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
18
src/main/java/com/baislsl/png/chunk/tRNS.java
Normal file
18
src/main/java/com/baislsl/png/chunk/tRNS.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,7 +5,8 @@ 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);
|
||||||
|
|
|
@ -16,9 +16,7 @@ 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;
|
||||||
|
@ -33,11 +31,10 @@ public class Decoder {
|
||||||
// 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().equals(chunkName)) {
|
if (chunkType.name().equalsIgnoreCase(chunkName)) {
|
||||||
chunkType.apply(png, length, type, data, crc);
|
chunkType.apply(png, length, type, data, crc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -88,13 +85,12 @@ public class Decoder {
|
||||||
return png;
|
return png;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private byte[] readBytes(int size) throws IOException {
|
private byte[] readBytes(int size) throws IOException {
|
||||||
byte[] result = new byte[size];
|
byte[] result = new byte[size];
|
||||||
int ret = in.read(result, 0, size);
|
int ret = in.read(result, 0, size);
|
||||||
if (ret == -1) return null;
|
if (ret == -1)
|
||||||
|
return null;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,14 @@ public class PNG {
|
||||||
public IHDR ihdr;
|
public IHDR ihdr;
|
||||||
public IDATManager idats = new IDATManager();
|
public IDATManager idats = new IDATManager();
|
||||||
public PLTE plte;
|
public PLTE plte;
|
||||||
|
public tRNS trns;
|
||||||
public IEND iend;
|
public IEND iend;
|
||||||
|
|
||||||
public PNG() {
|
public PNG() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PNG(IHDR ihdr, IDATManager idats, PLTE plte, IEND iend) {
|
public boolean isAlpha() {
|
||||||
this.ihdr = ihdr;
|
return this.trns != null || ihdr.getBpp() == 4;
|
||||||
this.idats = idats;
|
|
||||||
this.plte = plte;
|
|
||||||
this.iend = iend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getColor() throws DecodeException {
|
public int[] getColor() throws DecodeException {
|
||||||
|
@ -48,14 +46,16 @@ public class PNG {
|
||||||
switch (colorType) {
|
switch (colorType) {
|
||||||
case 2:
|
case 2:
|
||||||
if (bitDepth == 8) { // bpp = 3
|
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);
|
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 {
|
} else {
|
||||||
throw new DecodeException("not supported");
|
throw new DecodeException("not supported");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if (bitDepth == 8) { // bpp = 4
|
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;
|
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 {
|
} else {
|
||||||
throw new DecodeException("not supported");
|
throw new DecodeException("not supported");
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,13 @@ public class PNG {
|
||||||
case 3:
|
case 3:
|
||||||
int gap = 8 / bitDepth;
|
int gap = 8 / bitDepth;
|
||||||
int a = (1 << bitDepth) - 1;
|
int a = (1 << bitDepth) - 1;
|
||||||
colors[idx] = plte.getColor(data[i][j / gap] & a);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new DecodeException("Do not support color type " + colorType);
|
throw new DecodeException("Do not support color type " + colorType);
|
||||||
|
@ -103,6 +109,10 @@ public class PNG {
|
||||||
this.plte = plte;
|
this.plte = plte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTrns(tRNS trns) {
|
||||||
|
this.trns = trns;
|
||||||
|
}
|
||||||
|
|
||||||
public void setIend(IEND iend) {
|
public void setIend(IEND iend) {
|
||||||
this.iend = iend;
|
this.iend = iend;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ public class ByteHandler {
|
||||||
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) {
|
||||||
|
|
|
@ -20,7 +20,6 @@ public class CRC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static long updateCrc(long crc, byte[] buf, int size) {
|
private static long updateCrc(long crc, byte[] buf, int size) {
|
||||||
long ans = crc;
|
long ans = crc;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
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) {
|
private static int paethPredictor(int a, int b, int c) {
|
||||||
int p = a + b - c;
|
int p = a + b - c;
|
||||||
int pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - 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 (pa <= pb && pa <= pc)
|
||||||
if (pb <= pc) return b;
|
return a;
|
||||||
|
if (pb <= pc)
|
||||||
|
return b;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user