diff --git a/.checkstyle b/.checkstyle
new file mode 100644
index 0000000..428926e
--- /dev/null
+++ b/.checkstyle
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..cd01105
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d075be7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+/bin/
+/Operator/
+/DrawerProperties/
+*.pdfd
+*.dbc
+SchedulerConfig.txt
+scenicView.properties
+ScenariumConfig.txt
+*.class
+*~
+*.bck
+build.number
+/extern/
+/out/
+/.settings/
+/junit/
+/target/
diff --git a/.project b/.project
new file mode 100644
index 0000000..221dfab
--- /dev/null
+++ b/.project
@@ -0,0 +1,24 @@
+
+
+ atriasoft-png-decoder
+
+
+ atriasoft-png-decoder
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ net.sf.eclipsecs.core.CheckstyleBuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ net.sf.eclipsecs.core.CheckstyleNature
+
+
diff --git a/CheckStyle.xml b/CheckStyle.xml
new file mode 100755
index 0000000..d68aedd
--- /dev/null
+++ b/CheckStyle.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CleanUp.xml b/CleanUp.xml
new file mode 100644
index 0000000..543d7b0
--- /dev/null
+++ b/CleanUp.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Formatter.xml b/Formatter.xml
new file mode 100644
index 0000000..aec36fe
--- /dev/null
+++ b/Formatter.xml
@@ -0,0 +1,390 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3211fa6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+BSD licence
+===========
+
+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.
\ No newline at end of file
diff --git a/src/module-info.java b/src/module-info.java
new file mode 100644
index 0000000..60abdaa
--- /dev/null
+++ b/src/module-info.java
@@ -0,0 +1,10 @@
+/** Basic module interface.
+ *
+ * @author Edouard DUPIN */
+
+open module org.atriasoft.pngdecoder {
+ exports org.atriasoft.pngdecoder;
+
+ requires transitive org.atriasoft.egami;
+ requires transitive io.scenarium.logger;
+}
diff --git a/src/org/atriasoft/pngdecoder/PNGDecoder.java b/src/org/atriasoft/pngdecoder/PNGDecoder.java
new file mode 100644
index 0000000..a8bf785
--- /dev/null
+++ b/src/org/atriasoft/pngdecoder/PNGDecoder.java
@@ -0,0 +1,896 @@
+/*
+ * 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.atriasoft.pngdecoder;
+
+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 enum Format {
+ ABGR(4, true), ALPHA(1, true), BGRA(4, true), LUMINANCE(1, false), LUMINANCE_ALPHA(2, true), RGB(3, false), RGBA(4, true);
+
+ final boolean hasAlpha;
+ final int numComponents;
+
+ private Format(final int numComponents, final boolean hasAlpha) {
+ this.numComponents = numComponents;
+ this.hasAlpha = hasAlpha;
+ }
+
+ public int getNumComponents() {
+ return this.numComponents;
+ }
+
+ public boolean isHasAlpha() {
+ return this.hasAlpha;
+ }
+ }
+
+ private static final byte COLOR_GREYALPHA = 4;
+
+ private static final byte COLOR_GREYSCALE = 0;
+ private static final byte COLOR_INDEXED = 3;
+ private static final byte COLOR_TRUEALPHA = 6;
+ private static final byte COLOR_TRUECOLOR = 2;
+ private static final int IDAT = 0x49444154;
+
+ private static final int IEND = 0x49454E44;
+ private static final int IHDR = 0x49484452;
+ private static final int PLTE = 0x504C5445;
+ private static final byte[] SIGNATURE = { (byte) 137, 80, 78, 71, 13, 10, 26, 10 };
+ private static final int T_RNS = 0x74524E53;
+
+ private static boolean checkSignature(final byte[] buffer) {
+ for (int i = 0; i < PNGDecoder.SIGNATURE.length; i++) {
+ if (buffer[i] != PNGDecoder.SIGNATURE[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ private int bitdepth;
+ private final byte[] buffer;
+
+ private int bytesPerPixel;
+ private int chunkLength;
+ private int chunkRemaining;
+
+ private int chunkType;
+ private int colorType;
+ private final CRC32 crc;
+ private int height;
+ private final InputStream input;
+ private byte[] palette;
+ private byte[] paletteA;
+ private byte[] transPixel;
+
+ private int width;
+
+ public PNGDecoder(final InputStream input) throws IOException {
+ this.input = input;
+ this.crc = new CRC32();
+ this.buffer = new byte[4096];
+
+ readFully(this.buffer, 0, PNGDecoder.SIGNATURE.length);
+ if (!PNGDecoder.checkSignature(this.buffer)) {
+ throw new IOException("Not a valid PNG file");
+ }
+
+ openChunk(PNGDecoder.IHDR);
+ readIHDR();
+ closeChunk();
+
+ searchIDAT:
+ for (;;) {
+ openChunk();
+ switch (this.chunkType) {
+ default:
+ break;
+ case IDAT:
+ break searchIDAT;
+ case PLTE:
+ readPLTE();
+ break;
+ case T_RNS:
+ readtRNS();
+ break;
+ }
+ closeChunk();
+ }
+
+ if (this.colorType == PNGDecoder.COLOR_INDEXED && this.palette == null) {
+ throw new IOException("Missing PLTE chunk");
+ }
+ }
+
+ private void checkChunkLength(final int expected) throws IOException {
+ if (this.chunkLength != expected) {
+ throw new IOException("Chunk has wrong size");
+ }
+ }
+
+ private void closeChunk() throws IOException {
+ if (this.chunkRemaining > 0) {
+ // just skip the rest and the CRC
+ skip(this.chunkRemaining + 4);
+ } else {
+ readFully(this.buffer, 0, 4);
+ int expectedCrc = readInt(this.buffer, 0);
+ int computedCrc = (int) this.crc.getValue();
+ if (computedCrc != expectedCrc) {
+ throw new IOException("Invalid CRC");
+ }
+ }
+ this.chunkRemaining = 0;
+ this.chunkLength = 0;
+ this.chunkType = 0;
+ }
+
+ private void copy(final ByteBuffer buffer, final byte[] curLine) {
+ buffer.put(curLine, 1, curLine.length - 1);
+ }
+
+ private void copyPALtoABGR(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.paletteA != null) {
+ for (int i = 1, n = curLine.length; i < n; i += 1) {
+ int idx = curLine[i] & 255;
+ byte r = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = this.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 = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = (byte) 0xFF;
+ buffer.put(a).put(b).put(g).put(r);
+ }
+ }
+ }
+
+ private void copyPALtoBGRA(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.paletteA != null) {
+ for (int i = 1, n = curLine.length; i < n; i += 1) {
+ int idx = curLine[i] & 255;
+ byte r = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = this.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 = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = (byte) 0xFF;
+ buffer.put(b).put(g).put(r).put(a);
+ }
+ }
+ }
+
+ private void copyPALtoRGBA(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.paletteA != null) {
+ for (int i = 1, n = curLine.length; i < n; i += 1) {
+ int idx = curLine[i] & 255;
+ byte r = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = this.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 = this.palette[idx * 3 + 0];
+ byte g = this.palette[idx * 3 + 1];
+ byte b = this.palette[idx * 3 + 2];
+ byte a = (byte) 0xFF;
+ buffer.put(r).put(g).put(b).put(a);
+ }
+ }
+ }
+
+ private void copyRGBAtoABGR(final ByteBuffer buffer, final 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(final ByteBuffer buffer, final 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]).put(curLine[i + 3]);
+ }
+ }
+
+ private void copyRGBAtoRGB(final ByteBuffer buffer, final 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 copyRGBtoABGR(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.transPixel != null) {
+ byte tr = this.transPixel[1];
+ byte tg = this.transPixel[3];
+ byte tb = this.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 copyRGBtoBGRA(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.transPixel != null) {
+ byte tr = this.transPixel[1];
+ byte tg = this.transPixel[3];
+ byte tb = this.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 copyRGBtoRGBA(final ByteBuffer buffer, final byte[] curLine) {
+ if (this.transPixel != null) {
+ byte tr = this.transPixel[1];
+ byte tg = this.transPixel[3];
+ byte tb = this.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);
+ }
+ }
+ }
+
+ /**
+ * 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(final Format fmt) {
+ switch (this.colorType) {
+ case COLOR_TRUECOLOR:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA:
+ case RGB:
+ return fmt;
+ default:
+ return Format.RGB;
+ }
+ case COLOR_TRUEALPHA:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA:
+ case RGB:
+ return fmt;
+ default:
+ return Format.RGBA;
+ }
+ case COLOR_GREYSCALE:
+ switch (fmt) {
+ case LUMINANCE:
+ case ALPHA:
+ return fmt;
+ default:
+ return Format.LUMINANCE;
+ }
+ case COLOR_GREYALPHA:
+ return Format.LUMINANCE_ALPHA;
+ case COLOR_INDEXED:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA:
+ return fmt;
+ default:
+ return Format.RGBA;
+ }
+ default:
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+ }
+
+ /**
+ * Decodes the image into the specified buffer. The first line is placed at
+ * the current position. After decode the buffer position is at the end of
+ * the last line.
+ *
+ * @param buffer the buffer
+ * @param stride the stride in bytes from start of a line to start of the next line, can be negative.
+ * @param fmt the target format into which the image should be decoded.
+ * @throws IOException if a read or data error occurred
+ * @throws IllegalArgumentException if the start position of a line falls outside the buffer
+ * @throws UnsupportedOperationException if the image can't be decoded into the desired format
+ */
+ public void decode(final ByteBuffer buffer, final int stride, final Format fmt) throws IOException {
+ final int offset = buffer.position();
+ final int lineSize = ((this.width * this.bitdepth + 7) / 8) * this.bytesPerPixel;
+ byte[] curLine = new byte[lineSize + 1];
+ byte[] prevLine = new byte[lineSize + 1];
+ byte[] palLine = (this.bitdepth < 8) ? new byte[this.width + 1] : null;
+
+ final Inflater inflater = new Inflater();
+ try {
+ for (int y = 0; y < this.height; y++) {
+ readChunkUnzip(inflater, curLine, 0, curLine.length);
+ unfilter(curLine, prevLine);
+
+ buffer.position(offset + y * stride);
+
+ switch (this.colorType) {
+ case COLOR_TRUECOLOR:
+ switch (fmt) {
+ case ABGR:
+ copyRGBtoABGR(buffer, curLine);
+ break;
+ case RGBA:
+ copyRGBtoRGBA(buffer, curLine);
+ break;
+ case BGRA:
+ copyRGBtoBGRA(buffer, curLine);
+ break;
+ case RGB:
+ copy(buffer, curLine);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_TRUEALPHA:
+ switch (fmt) {
+ case ABGR:
+ copyRGBAtoABGR(buffer, curLine);
+ break;
+ case RGBA:
+ copy(buffer, curLine);
+ break;
+ case BGRA:
+ copyRGBAtoBGRA(buffer, curLine);
+ break;
+ case RGB:
+ copyRGBAtoRGB(buffer, curLine);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_GREYSCALE:
+ switch (fmt) {
+ case LUMINANCE:
+ case ALPHA:
+ copy(buffer, curLine);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_GREYALPHA:
+ switch (fmt) {
+ case LUMINANCE_ALPHA:
+ copy(buffer, curLine);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_INDEXED:
+ switch (this.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");
+ }
+ switch (fmt) {
+ case ABGR:
+ copyPALtoABGR(buffer, palLine);
+ break;
+ case RGBA:
+ copyPALtoRGBA(buffer, palLine);
+ break;
+ case BGRA:
+ copyPALtoBGRA(buffer, palLine);
+ break;
+ default:
+ 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();
+ }
+ }
+
+ /**
+ * Decodes the image into the specified buffer. The last line is placed at
+ * the current position. After decode the buffer position is at the end of
+ * the first line.
+ *
+ * @param buffer the buffer
+ * @param stride the stride in bytes from start of a line to start of the next line, must be positive.
+ * @param fmt the target format into which the image should be decoded.
+ * @throws IOException if a read or data error occurred
+ * @throws IllegalArgumentException if the start position of a line falls outside the buffer
+ * @throws UnsupportedOperationException if the image can't be decoded into the desired format
+ */
+ public void decodeFlipped(final ByteBuffer buffer, final int stride, final Format fmt) throws IOException {
+ if (stride <= 0) {
+ throw new IllegalArgumentException("stride");
+ }
+ int pos = buffer.position();
+ int posDelta = (this.height - 1) * stride;
+ buffer.position(pos + posDelta);
+ decode(buffer, -stride, fmt);
+ buffer.position(buffer.position() + posDelta);
+ }
+
+ private void expand1(final byte[] src, final 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 expand2(final byte[] src, final 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 expand4(final byte[] src, final 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);
+ }
+ }
+ }
+
+ public int getHeight() {
+ return this.height;
+ }
+
+ public int getWidth() {
+ return this.width;
+ }
+
+ /**
+ * Checks if the image has transparency information either from
+ * an alpha channel or from a tRNS chunk.
+ *
+ * @return true if the image has transparency
+ * @see #hasAlphaChannel()
+ * @see #overwriteTRNS(byte, byte, byte)
+ */
+ public boolean hasAlpha() {
+ return hasAlphaChannel() || this.paletteA != null || this.transPixel != null;
+ }
+
+ /**
+ * Checks if the image has a real alpha channel.
+ * This method does not check for the presence of a tRNS chunk.
+ *
+ * @return true if the image has an alpha channel
+ * @see #hasAlpha()
+ */
+ public boolean hasAlphaChannel() {
+ return this.colorType == PNGDecoder.COLOR_TRUEALPHA || this.colorType == PNGDecoder.COLOR_GREYALPHA;
+ }
+
+ public boolean isRGB() {
+ return this.colorType == PNGDecoder.COLOR_TRUEALPHA || this.colorType == PNGDecoder.COLOR_TRUECOLOR || this.colorType == PNGDecoder.COLOR_INDEXED;
+ }
+
+ private void openChunk() throws IOException {
+ readFully(this.buffer, 0, 8);
+ this.chunkLength = readInt(this.buffer, 0);
+ this.chunkType = readInt(this.buffer, 4);
+ this.chunkRemaining = this.chunkLength;
+ this.crc.reset();
+ this.crc.update(this.buffer, 4, 4); // only chunkType
+ }
+
+ private void openChunk(final int expected) throws IOException {
+ openChunk();
+ if (this.chunkType != expected) {
+ throw new IOException("Expected chunk: " + Integer.toHexString(expected));
+ }
+ }
+
+ /**
+ * Overwrites the tRNS chunk entry to make a selected color transparent.
+ *
This can only be invoked when the image has no alpha channel.
+ * Calling this method causes {@link #hasAlpha()} to return true.
+ *
+ * @param r the red component of the color to make transparent
+ * @param g the green component of the color to make transparent
+ * @param b the blue component of the color to make transparent
+ * @throws UnsupportedOperationException if the tRNS chunk data can't be set
+ * @see #hasAlphaChannel()
+ */
+ public void overwriteTRNS(final byte r, final byte g, final byte b) {
+ if (hasAlphaChannel()) {
+ throw new UnsupportedOperationException("image has an alpha channel");
+ }
+ byte[] pal = this.palette;
+ if (pal == null) {
+ this.transPixel = new byte[] { 0, r, 0, g, 0, b };
+ } else {
+ this.paletteA = new byte[pal.length / 3];
+ for (int i = 0, j = 0; i < pal.length; i += 3, j++) {
+ if (pal[i] != r || pal[i + 1] != g || pal[i + 2] != b) {
+ this.paletteA[j] = (byte) 0xFF;
+ }
+ }
+ }
+ }
+
+ private int readChunk(final byte[] buffer, final int offset, int length) throws IOException {
+ if (length > this.chunkRemaining) {
+ length = this.chunkRemaining;
+ }
+ readFully(buffer, offset, length);
+ this.crc.update(buffer, offset, length);
+ this.chunkRemaining -= length;
+ return length;
+ }
+
+ private void readChunkUnzip(final Inflater inflater, final byte[] buffer, int offset, int length) throws IOException {
+ assert (buffer != this.buffer);
+ try {
+ do {
+ int read = inflater.inflate(buffer, offset, length);
+ if (read <= 0) {
+ if (inflater.finished()) {
+ throw new EOFException();
+ }
+ if (!inflater.needsInput()) {
+ throw new IOException("Can't inflate " + length + " bytes");
+ }
+ refillInflater(inflater);
+ } else {
+ offset += read;
+ length -= read;
+ }
+ } while (length > 0);
+ } catch (DataFormatException ex) {
+ throw (IOException) (new IOException("inflate error").initCause(ex));
+ }
+ }
+
+ private void readFully(final byte[] buffer, int offset, int length) throws IOException {
+ do {
+ int read = this.input.read(buffer, offset, length);
+ if (read < 0) {
+ throw new EOFException();
+ }
+ offset += read;
+ length -= read;
+ } while (length > 0);
+ }
+
+ private void readIHDR() throws IOException {
+ checkChunkLength(13);
+ readChunk(this.buffer, 0, 13);
+ this.width = readInt(this.buffer, 0);
+ this.height = readInt(this.buffer, 4);
+ this.bitdepth = this.buffer[8] & 255;
+ this.colorType = this.buffer[9] & 255;
+
+ switch (this.colorType) {
+ case COLOR_GREYSCALE:
+ if (this.bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + this.bitdepth);
+ }
+ this.bytesPerPixel = 1;
+ break;
+ case COLOR_GREYALPHA:
+ if (this.bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + this.bitdepth);
+ }
+ this.bytesPerPixel = 2;
+ break;
+ case COLOR_TRUECOLOR:
+ if (this.bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + this.bitdepth);
+ }
+ this.bytesPerPixel = 3;
+ break;
+ case COLOR_TRUEALPHA:
+ if (this.bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + this.bitdepth);
+ }
+ this.bytesPerPixel = 4;
+ break;
+ case COLOR_INDEXED:
+ switch (this.bitdepth) {
+ case 8:
+ case 4:
+ case 2:
+ case 1:
+ this.bytesPerPixel = 1;
+ break;
+ default:
+ throw new IOException("Unsupported bit depth: " + this.bitdepth);
+ }
+ break;
+ default:
+ throw new IOException("unsupported color format: " + this.colorType);
+ }
+
+ if (this.buffer[10] != 0) {
+ throw new IOException("unsupported compression method");
+ }
+ if (this.buffer[11] != 0) {
+ throw new IOException("unsupported filtering method");
+ }
+ if (this.buffer[12] != 0) {
+ throw new IOException("unsupported interlace method");
+ }
+ }
+
+ private int readInt(final byte[] buffer, final int offset) {
+ return ((buffer[offset]) << 24) | ((buffer[offset + 1] & 255) << 16) | ((buffer[offset + 2] & 255) << 8) | ((buffer[offset + 3] & 255));
+ }
+
+ private void readPLTE() throws IOException {
+ int paletteEntries = this.chunkLength / 3;
+ if (paletteEntries < 1 || paletteEntries > 256 || (this.chunkLength % 3) != 0) {
+ throw new IOException("PLTE chunk has wrong length");
+ }
+ this.palette = new byte[paletteEntries * 3];
+ readChunk(this.palette, 0, this.palette.length);
+ }
+
+ private void readtRNS() throws IOException {
+ switch (this.colorType) {
+ case COLOR_GREYSCALE:
+ checkChunkLength(2);
+ this.transPixel = new byte[2];
+ readChunk(this.transPixel, 0, 2);
+ break;
+ case COLOR_TRUECOLOR:
+ checkChunkLength(6);
+ this.transPixel = new byte[6];
+ readChunk(this.transPixel, 0, 6);
+ break;
+ case COLOR_INDEXED:
+ if (this.palette == null) {
+ throw new IOException("tRNS chunk without PLTE chunk");
+ }
+ this.paletteA = new byte[this.palette.length / 3];
+ Arrays.fill(this.paletteA, (byte) 0xFF);
+ readChunk(this.paletteA, 0, this.paletteA.length);
+ break;
+ default:
+ // just ignore it
+ }
+ }
+
+ private void refillInflater(final Inflater inflater) throws IOException {
+ while (this.chunkRemaining == 0) {
+ closeChunk();
+ openChunk(PNGDecoder.IDAT);
+ }
+ int read = readChunk(this.buffer, 0, this.buffer.length);
+ inflater.setInput(this.buffer, 0, read);
+ }
+
+ private void skip(long amount) throws IOException {
+ while (amount > 0) {
+ long skipped = this.input.skip(amount);
+ if (skipped < 0) {
+ throw new EOFException();
+ }
+ amount -= skipped;
+ }
+ }
+
+ private void unfilter(final byte[] curLine, final 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 unfilterAverage(final byte[] curLine, final 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(final byte[] curLine, final 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 unfilterSub(final 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(final byte[] curLine, final byte[] prevLine) {
+ final int bpp = this.bytesPerPixel;
+ for (int i = 1, n = curLine.length; i < n; ++i) {
+ curLine[i] += prevLine[i];
+ }
+ }
+}
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..6c6aa7c
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+0.1.0
\ No newline at end of file