diff --git a/AA2Data/AA2Card.cs b/AA2Data/AA2Card.cs
index 873b70c..1768002 100644
--- a/AA2Data/AA2Card.cs
+++ b/AA2Data/AA2Card.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
+using PNGNet;
namespace AA2Data
{
@@ -16,16 +17,40 @@ public byte[] raw
get
{
byte[] buffer = new byte[_image.Length + Offset];
- using (MemoryStream mem = new MemoryStream(_image.Length + Offset))
+ using (MemoryStream mem = new MemoryStream()) //_image.Length + Offset
using (BinaryWriter bw = new BinaryWriter(mem))
{
+ //AA2Unlimited chunk
+ using (MemoryStream ms = new MemoryStream(_image))
+ using (MemoryStream ex = new MemoryStream())
+ {
+ var img = new PNGImage(ms);
+
+ bool containsChunk = img.Chunks.Any(x => x.Type == "aaUd");
+
+ if (!containsChunk && AA2UChunk != null)
+ {
+ img.Chunks.Insert(img.Chunks.FindIndex(x => x.Type == "IEND"), AA2UChunk);
+ }
+ else if (AA2UChunk != null)
+ {
+ int index = img.Chunks.FindIndex(x => x.Type == "aaUd");
+ img.Chunks.RemoveAt(index);
+ img.Chunks.Insert(index, AA2UChunk);
+ }
+
+ img.Write(ex, false);
+
+ _image = ex.ToArray();
+ }
+
bw.Write(_image);
bw.Write(data.raw);
bw.Write(RosterLength);
bw.Write(_RosterImage);
bw.Write(Offset);
- mem.Position = 0;
- mem.Read(buffer, 0, (int)mem.Length);
+
+ buffer = mem.ToArray();
}
return buffer;
}
@@ -34,17 +59,28 @@ public byte[] raw
using (MemoryStream mem = new MemoryStream(value))
using (BinaryReader br = new BinaryReader(mem))
{
- br.BaseStream.Seek(-4, SeekOrigin.End);
- int offset = br.ReadInt32();
- br.BaseStream.Seek(0, SeekOrigin.Begin);
+ br.BaseStream.Seek(-4, SeekOrigin.End);
+ int offset = br.ReadInt32();
+ br.BaseStream.Seek(0, SeekOrigin.Begin);
- _image = br.ReadBytes((int)br.BaseStream.Length - offset);
- data.raw = br.ReadBytes(3011);
- int length = br.ReadInt32();
- _RosterImage = br.ReadBytes(length);
+ _image = br.ReadBytes((int)br.BaseStream.Length - offset);
+
+ //AA2Unlimited chunk
+ using (MemoryStream ms = new MemoryStream(_image))
+ {
+ var img = new PNGImage(ms);
+
+ AA2UChunk = img.Chunks.DefaultIfEmpty(null).FirstOrDefault(x => x.Type == "aaUd");
+ }
+
+ data.raw = br.ReadBytes(3011);
+ int length = br.ReadInt32();
+ _RosterImage = br.ReadBytes(length);
}
}
}
+
+ private Chunk AA2UChunk = null;
private byte[] _image;
public Image Image
diff --git a/AA2Data/AA2Data.csproj b/AA2Data/AA2Data.csproj
index 845e60c..4bb178f 100644
--- a/AA2Data/AA2Data.csproj
+++ b/AA2Data/AA2Data.csproj
@@ -67,6 +67,12 @@
+
+
+ {d5cb46e9-e69b-4b45-a15a-c0087833dd4d}
+ PNGNet
+
+
INIT_FINISHING_STATE ---.
+ * / | (2) (5) |
+ * / v (5) |
+ * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
+ * \ | (3) | ,-------'
+ * | | | (3) /
+ * v v (5) v v
+ * (1) -> BUSY_STATE ----> FINISHING_STATE
+ * | (6)
+ * v
+ * FINISHED_STATE
+ * \_____________________________________/
+ * | (7)
+ * v
+ * CLOSED_STATE
+ *
+ * (1) If we should produce a header we start in INIT_STATE, otherwise
+ * we start in BUSY_STATE.
+ * (2) A dictionary may be set only when we are in INIT_STATE, then
+ * we change the state as indicated.
+ * (3) Whether a dictionary is set or not, on the first call of deflate
+ * we change to BUSY_STATE.
+ * (4) -- intentionally left blank -- :)
+ * (5) FINISHING_STATE is entered, when flush() is called to indicate that
+ * there is no more INPUT. There are also states indicating, that
+ * the header wasn't written yet.
+ * (6) FINISHED_STATE is entered, when everything has been flushed to the
+ * internal pending output buffer.
+ * (7) At any time (7)
+ *
+ */
+
+ private static int IS_SETDICT = 0x01;
+ private static int IS_FLUSHING = 0x04;
+ private static int IS_FINISHING = 0x08;
+
+ private static int INIT_STATE = 0x00;
+ private static int SETDICT_STATE = 0x01;
+// private static int INIT_FINISHING_STATE = 0x08;
+// private static int SETDICT_FINISHING_STATE = 0x09;
+ private static int BUSY_STATE = 0x10;
+ private static int FLUSHING_STATE = 0x14;
+ private static int FINISHING_STATE = 0x1c;
+ private static int FINISHED_STATE = 0x1e;
+ private static int CLOSED_STATE = 0x7f;
+
+ ///
+ /// Compression level.
+ ///
+ private int level;
+
+ ///
+ /// should we include a header.
+ ///
+ private bool noHeader;
+
+// ///
+// /// Compression strategy.
+// ///
+// private int strategy;
+
+ ///
+ /// The current state.
+ ///
+ private int state;
+
+ ///
+ /// The total bytes of output written.
+ ///
+ private int totalOut;
+
+ ///
+ /// The pending output.
+ ///
+ private DeflaterPending pending;
+
+ ///
+ /// The deflater engine.
+ ///
+ private DeflaterEngine engine;
+
+ ///
+ /// Creates a new deflater with default compression level.
+ ///
+ public Deflater() : this(DEFAULT_COMPRESSION, false)
+ {
+
+ }
+
+ ///
+ /// Creates a new deflater with given compression level.
+ ///
+ ///
+ /// the compression level, a value between NO_COMPRESSION
+ /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
+ ///
+ /// if lvl is out of range.
+ public Deflater(int lvl) : this(lvl, false)
+ {
+
+ }
+
+ ///
+ /// Creates a new deflater with given compression level.
+ ///
+ ///
+ /// the compression level, a value between NO_COMPRESSION
+ /// and BEST_COMPRESSION.
+ ///
+ ///
+ /// true, if we should suppress the deflate header at the
+ /// beginning and the adler checksum at the end of the output. This is
+ /// useful for the GZIP format.
+ ///
+ /// if lvl is out of range.
+ public Deflater(int lvl, bool nowrap)
+ {
+ if (lvl == DEFAULT_COMPRESSION) {
+ lvl = 6;
+ } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {
+ throw new ArgumentOutOfRangeException("lvl");
+ }
+
+ pending = new DeflaterPending();
+ engine = new DeflaterEngine(pending);
+ this.noHeader = nowrap;
+ SetStrategy(DeflateStrategy.Default);
+ SetLevel(lvl);
+ Reset();
+ }
+
+
+ ///
+ /// Resets the deflater. The deflater acts afterwards as if it was
+ /// just created with the same compression level and strategy as it
+ /// had before.
+ ///
+ public void Reset()
+ {
+ state = (noHeader ? BUSY_STATE : INIT_STATE);
+ totalOut = 0;
+ pending.Reset();
+ engine.Reset();
+ }
+
+ ///
+ /// Gets the current adler checksum of the data that was processed so far.
+ ///
+ public int Adler {
+ get {
+ return engine.Adler;
+ }
+ }
+
+ ///
+ /// Gets the number of input bytes processed so far.
+ ///
+ public int TotalIn {
+ get {
+ return engine.TotalIn;
+ }
+ }
+
+ ///
+ /// Gets the number of output bytes so far.
+ ///
+ public int TotalOut {
+ get {
+ return totalOut;
+ }
+ }
+
+ ///
+ /// Flushes the current input block. Further calls to deflate() will
+ /// produce enough output to inflate everything in the current input
+ /// block. This is not part of Sun's JDK so I have made it package
+ /// private. It is used by DeflaterOutputStream to implement
+ /// flush().
+ ///
+ public void Flush()
+ {
+ state |= IS_FLUSHING;
+ }
+
+ ///
+ /// Finishes the deflater with the current input block. It is an error
+ /// to give more input after this method was called. This method must
+ /// be called to force all bytes to be flushed.
+ ///
+ public void Finish()
+ {
+ state |= IS_FLUSHING | IS_FINISHING;
+ }
+
+ ///
+ /// Returns true if the stream was finished and no more output bytes
+ /// are available.
+ ///
+ public bool IsFinished {
+ get {
+ return state == FINISHED_STATE && pending.IsFlushed;
+ }
+ }
+
+ ///
+ /// Returns true, if the input buffer is empty.
+ /// You should then call setInput().
+ /// NOTE: This method can also return true when the stream
+ /// was finished.
+ ///
+ public bool IsNeedingInput {
+ get {
+ return engine.NeedsInput();
+ }
+ }
+
+ ///
+ /// Sets the data which should be compressed next. This should be only
+ /// called when needsInput indicates that more input is needed.
+ /// If you call setInput when needsInput() returns false, the
+ /// previous input that is still pending will be thrown away.
+ /// The given byte array should not be changed, before needsInput() returns
+ /// true again.
+ /// This call is equivalent to setInput(input, 0, input.length)
.
+ ///
+ ///
+ /// the buffer containing the input data.
+ ///
+ ///
+ /// if the buffer was finished() or ended().
+ ///
+ public void SetInput(byte[] input)
+ {
+ SetInput(input, 0, input.Length);
+ }
+
+ ///
+ /// Sets the data which should be compressed next. This should be
+ /// only called when needsInput indicates that more input is needed.
+ /// The given byte array should not be changed, before needsInput() returns
+ /// true again.
+ ///
+ ///
+ /// the buffer containing the input data.
+ ///
+ ///
+ /// the start of the data.
+ ///
+ ///
+ /// the length of the data.
+ ///
+ ///
+ /// if the buffer was finished() or ended() or if previous input is still pending.
+ ///
+ public void SetInput(byte[] input, int off, int len)
+ {
+ if ((state & IS_FINISHING) != 0) {
+ throw new InvalidOperationException("finish()/end() already called");
+ }
+ engine.SetInput(input, off, len);
+ }
+
+ ///
+ /// Sets the compression level. There is no guarantee of the exact
+ /// position of the change, but if you call this when needsInput is
+ /// true the change of compression level will occur somewhere near
+ /// before the end of the so far given input.
+ ///
+ ///
+ /// the new compression level.
+ ///
+ public void SetLevel(int lvl)
+ {
+ if (lvl == DEFAULT_COMPRESSION) {
+ lvl = 6;
+ } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {
+ throw new ArgumentOutOfRangeException("lvl");
+ }
+
+
+ if (level != lvl) {
+ level = lvl;
+ engine.SetLevel(lvl);
+ }
+ }
+
+ ///
+ /// Sets the compression strategy. Strategy is one of
+ /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
+ /// position where the strategy is changed, the same as for
+ /// setLevel() applies.
+ ///
+ ///
+ /// the new compression strategy.
+ ///
+ public void SetStrategy(DeflateStrategy stgy)
+ {
+ engine.Strategy = stgy;
+ }
+
+ ///
+ /// Deflates the current input block to the given array. It returns
+ /// the number of bytes compressed, or 0 if either
+ /// needsInput() or finished() returns true or length is zero.
+ ///
+ ///
+ /// the buffer where to write the compressed data.
+ ///
+ public int Deflate(byte[] output)
+ {
+ return Deflate(output, 0, output.Length);
+ }
+
+ ///
+ /// Deflates the current input block to the given array. It returns
+ /// the number of bytes compressed, or 0 if either
+ /// needsInput() or finished() returns true or length is zero.
+ ///
+ ///
+ /// the buffer where to write the compressed data.
+ ///
+ ///
+ /// the offset into the output array.
+ ///
+ ///
+ /// the maximum number of bytes that may be written.
+ ///
+ ///
+ /// if end() was called.
+ ///
+ ///
+ /// if offset and/or length don't match the array length.
+ ///
+ public int Deflate(byte[] output, int offset, int length)
+ {
+ int origLength = length;
+
+ if (state == CLOSED_STATE) {
+ throw new InvalidOperationException("Deflater closed");
+ }
+
+ if (state < BUSY_STATE) {
+ /* output header */
+ int header = (DEFLATED +
+ ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
+ int level_flags = (level - 1) >> 1;
+ if (level_flags < 0 || level_flags > 3) {
+ level_flags = 3;
+ }
+ header |= level_flags << 6;
+ if ((state & IS_SETDICT) != 0) {
+ /* Dictionary was set */
+ header |= DeflaterConstants.PRESET_DICT;
+ }
+ header += 31 - (header % 31);
+
+
+ pending.WriteShortMSB(header);
+ if ((state & IS_SETDICT) != 0) {
+ int chksum = engine.Adler;
+ engine.ResetAdler();
+ pending.WriteShortMSB(chksum >> 16);
+ pending.WriteShortMSB(chksum & 0xffff);
+ }
+
+ state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
+ }
+
+ for (;;) {
+ int count = pending.Flush(output, offset, length);
+ offset += count;
+ totalOut += count;
+ length -= count;
+
+ if (length == 0 || state == FINISHED_STATE) {
+ break;
+ }
+
+ if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) {
+ if (state == BUSY_STATE) {
+ /* We need more input now */
+ return origLength - length;
+ } else if (state == FLUSHING_STATE) {
+ if (level != NO_COMPRESSION) {
+ /* We have to supply some lookahead. 8 bit lookahead
+ * are needed by the zlib inflater, and we must fill
+ * the next byte, so that all bits are flushed.
+ */
+ int neededbits = 8 + ((-pending.BitCount) & 7);
+ while (neededbits > 0) {
+ /* write a static tree block consisting solely of
+ * an EOF:
+ */
+ pending.WriteBits(2, 10);
+ neededbits -= 10;
+ }
+ }
+ state = BUSY_STATE;
+ } else if (state == FINISHING_STATE) {
+ pending.AlignToByte();
+ /* We have completed the stream */
+ if (!noHeader) {
+ int adler = engine.Adler;
+ pending.WriteShortMSB(adler >> 16);
+ pending.WriteShortMSB(adler & 0xffff);
+ }
+ state = FINISHED_STATE;
+ }
+ }
+ }
+ return origLength - length;
+ }
+
+ ///
+ /// Sets the dictionary which should be used in the deflate process.
+ /// This call is equivalent to setDictionary(dict, 0, dict.Length)
.
+ ///
+ ///
+ /// the dictionary.
+ ///
+ ///
+ /// if setInput () or deflate () were already called or another dictionary was already set.
+ ///
+ public void SetDictionary(byte[] dict)
+ {
+ SetDictionary(dict, 0, dict.Length);
+ }
+
+ ///
+ /// Sets the dictionary which should be used in the deflate process.
+ /// The dictionary should be a byte array containing strings that are
+ /// likely to occur in the data which should be compressed. The
+ /// dictionary is not stored in the compressed output, only a
+ /// checksum. To decompress the output you need to supply the same
+ /// dictionary again.
+ ///
+ ///
+ /// the dictionary.
+ ///
+ ///
+ /// an offset into the dictionary.
+ ///
+ ///
+ /// the length of the dictionary.
+ ///
+ ///
+ /// if setInput () or deflate () were already called or another dictionary was already set.
+ ///
+ public void SetDictionary(byte[] dict, int offset, int length)
+ {
+ if (state != INIT_STATE) {
+ throw new InvalidOperationException();
+ }
+
+ state = SETDICT_STATE;
+ engine.SetDictionary(dict, offset, length);
+ }
+ }
+}
diff --git a/PNGNet/Compression/DeflaterConstants.cs b/PNGNet/Compression/DeflaterConstants.cs
new file mode 100644
index 0000000..6d73200
--- /dev/null
+++ b/PNGNet/Compression/DeflaterConstants.cs
@@ -0,0 +1,84 @@
+// DeflaterConstants.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ ///
+ /// This class contains constants used for the deflater.
+ ///
+ public class DeflaterConstants
+ {
+ public const bool DEBUGGING = false;
+
+ public const int STORED_BLOCK = 0;
+ public const int STATIC_TREES = 1;
+ public const int DYN_TREES = 2;
+ public const int PRESET_DICT = 0x20;
+
+ public const int DEFAULT_MEM_LEVEL = 8;
+
+ public const int MAX_MATCH = 258;
+ public const int MIN_MATCH = 3;
+
+ public const int MAX_WBITS = 15;
+ public const int WSIZE = 1 << MAX_WBITS;
+ public const int WMASK = WSIZE - 1;
+
+ public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
+ public const int HASH_SIZE = 1 << HASH_BITS;
+ public const int HASH_MASK = HASH_SIZE - 1;
+ public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
+
+ public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
+ public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
+
+ public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
+ public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE-5);
+
+ public const int DEFLATE_STORED = 0;
+ public const int DEFLATE_FAST = 1;
+ public const int DEFLATE_SLOW = 2;
+
+ public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 };
+ public static int[] MAX_LAZY = { 0, 4, 5, 6, 4,16, 16, 32, 128, 258 };
+ public static int[] NICE_LENGTH = { 0, 8,16,32,16,32,128,128, 258, 258 };
+ public static int[] MAX_CHAIN = { 0, 4, 8,32,16,32,128,256,1024,4096 };
+ public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 };
+ }
+}
diff --git a/PNGNet/Compression/DeflaterEngine.cs b/PNGNet/Compression/DeflaterEngine.cs
new file mode 100644
index 0000000..6c9d4e2
--- /dev/null
+++ b/PNGNet/Compression/DeflaterEngine.cs
@@ -0,0 +1,650 @@
+// DeflaterEngine.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+using ICSharpCode.SharpZipLib.Checksums;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ public enum DeflateStrategy {
+ // The default strategy.
+ Default = 0,
+
+ // This strategy will only allow longer string repetitions. It is
+ // useful for random data with a small character set.
+ Filtered = 1,
+
+ // This strategy will not look for string repetitions at all. It
+ // only encodes with Huffman trees (which means, that more common
+ // characters get a smaller encoding.
+ HuffmanOnly = 2
+ }
+
+ public class DeflaterEngine : DeflaterConstants
+ {
+ static int TOO_FAR = 4096;
+
+ int ins_h;
+// private byte[] buffer;
+ short[] head;
+ short[] prev;
+
+ int matchStart, matchLen;
+ bool prevAvailable;
+ int blockStart;
+ int strstart, lookahead;
+ byte[] window;
+
+ DeflateStrategy strategy;
+ int max_chain, max_lazy, niceLength, goodLength;
+
+ ///
+ /// The current compression function.
+ ///
+ int comprFunc;
+
+ ///
+ /// The input data for compression.
+ ///
+ byte[] inputBuf;
+
+ ///
+ /// The total bytes of input read.
+ ///
+ int totalIn;
+
+ ///
+ /// The offset into inputBuf, where input data starts.
+ ///
+ int inputOff;
+
+ ///
+ /// The end offset of the input data.
+ ///
+ int inputEnd;
+
+ DeflaterPending pending;
+ DeflaterHuffman huffman;
+
+ ///
+ /// The adler checksum
+ ///
+ Adler32 adler;
+
+ public DeflaterEngine(DeflaterPending pending)
+ {
+ this.pending = pending;
+ huffman = new DeflaterHuffman(pending);
+ adler = new Adler32();
+
+ window = new byte[2 * WSIZE];
+ head = new short[HASH_SIZE];
+ prev = new short[WSIZE];
+
+ /* We start at index 1, to avoid a implementation deficiency, that
+ * we cannot build a repeat pattern at index 0.
+ */
+ blockStart = strstart = 1;
+ }
+
+ public void Reset()
+ {
+ huffman.Reset();
+ adler.Reset();
+ blockStart = strstart = 1;
+ lookahead = 0;
+ totalIn = 0;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+
+ for (int i = 0; i < HASH_SIZE; i++) {
+ head[i] = 0;
+ }
+
+ for (int i = 0; i < WSIZE; i++) {
+ prev[i] = 0;
+ }
+ }
+
+ public void ResetAdler()
+ {
+ adler.Reset();
+ }
+
+ public int Adler {
+ get {
+ return (int)adler.Value;
+ }
+ }
+
+ public int TotalIn {
+ get {
+ return totalIn;
+ }
+ }
+
+ public DeflateStrategy Strategy {
+ get {
+ return strategy;
+ }
+ set {
+ strategy = value;
+ }
+ }
+
+ public void SetLevel(int lvl)
+ {
+ goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
+ max_lazy = DeflaterConstants.MAX_LAZY[lvl];
+ niceLength = DeflaterConstants.NICE_LENGTH[lvl];
+ max_chain = DeflaterConstants.MAX_CHAIN[lvl];
+
+ if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) {
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("Change from "+comprFunc +" to "
+// + DeflaterConstants.COMPR_FUNC[lvl]);
+// }
+ switch (comprFunc) {
+ case DEFLATE_STORED:
+ if (strstart > blockStart) {
+ huffman.FlushStoredBlock(window, blockStart,
+ strstart - blockStart, false);
+ blockStart = strstart;
+ }
+ UpdateHash();
+ break;
+ case DEFLATE_FAST:
+ if (strstart > blockStart) {
+ huffman.FlushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ break;
+ case DEFLATE_SLOW:
+ if (prevAvailable) {
+ huffman.TallyLit(window[strstart-1] & 0xff);
+ }
+ if (strstart > blockStart) {
+ huffman.FlushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ break;
+ }
+ comprFunc = COMPR_FUNC[lvl];
+ }
+ }
+
+ private void UpdateHash()
+ {
+// if (DEBUGGING) {
+// Console.WriteLine("updateHash: "+strstart);
+// }
+ ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
+ }
+
+ private int InsertString()
+ {
+ short match;
+ int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) & HASH_MASK;
+
+// if (DEBUGGING) {
+// if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^
+// (window[strstart + 1] << HASH_SHIFT) ^
+// (window[strstart + 2])) & HASH_MASK)) {
+// throw new Exception("hash inconsistent: "+hash+"/"
+// +window[strstart]+","
+// +window[strstart+1]+","
+// +window[strstart+2]+","+HASH_SHIFT);
+// }
+// }
+
+ prev[strstart & WMASK] = match = head[hash];
+ head[hash] = (short)strstart;
+ ins_h = hash;
+ return match & 0xffff;
+ }
+
+ void SlideWindow()
+ {
+ Array.Copy(window, WSIZE, window, 0, WSIZE);
+ matchStart -= WSIZE;
+ strstart -= WSIZE;
+ blockStart -= WSIZE;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ * at the expense of memory usage).
+ */
+ for (int i = 0; i < HASH_SIZE; ++i) {
+ int m = head[i] & 0xffff;
+ head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
+ }
+
+ /* Slide the prev table. */
+ for (int i = 0; i < WSIZE; i++) {
+ int m = prev[i] & 0xffff;
+ prev[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
+ }
+ }
+
+ public void FillWindow()
+ {
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (strstart >= WSIZE + MAX_DIST) {
+ SlideWindow();
+ }
+
+ /* If there is not enough lookahead, but still some input left,
+ * read in the input
+ */
+ while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) {
+ int more = 2 * WSIZE - lookahead - strstart;
+
+ if (more > inputEnd - inputOff) {
+ more = inputEnd - inputOff;
+ }
+
+ System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more);
+ adler.Update(inputBuf, inputOff, more);
+
+ inputOff += more;
+ totalIn += more;
+ lookahead += more;
+ }
+
+ if (lookahead >= MIN_MATCH) {
+ UpdateHash();
+ }
+ }
+
+ private bool FindLongestMatch(int curMatch)
+ {
+ int chainLength = this.max_chain;
+ int niceLength = this.niceLength;
+ short[] prev = this.prev;
+ int scan = this.strstart;
+ int match;
+ int best_end = this.strstart + matchLen;
+ int best_len = Math.Max(matchLen, MIN_MATCH - 1);
+
+ int limit = Math.Max(strstart - MAX_DIST, 0);
+
+ int strend = strstart + MAX_MATCH - 1;
+ byte scan_end1 = window[best_end - 1];
+ byte scan_end = window[best_end];
+
+ /* Do not waste too much time if we already have a good match: */
+ if (best_len >= this.goodLength) {
+ chainLength >>= 2;
+ }
+
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if (niceLength > lookahead) {
+ niceLength = lookahead;
+ }
+
+ if (DeflaterConstants.DEBUGGING && strstart > 2 * WSIZE - MIN_LOOKAHEAD) {
+ throw new InvalidOperationException("need lookahead");
+ }
+
+ do {
+ if (DeflaterConstants.DEBUGGING && curMatch >= strstart) {
+ throw new InvalidOperationException("future match");
+ }
+ if (window[curMatch + best_len] != scan_end ||
+ window[curMatch + best_len - 1] != scan_end1 ||
+ window[curMatch] != window[scan] ||
+ window[curMatch + 1] != window[scan + 1]) {
+ continue;
+ }
+
+ match = curMatch + 2;
+ scan += 2;
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ while (window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] &&
+ window[++scan] == window[++match] && scan < strend) ;
+
+ if (scan > best_end) {
+ // if (DeflaterConstants.DEBUGGING && ins_h == 0)
+ // System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
+ matchStart = curMatch;
+ best_end = scan;
+ best_len = scan - strstart;
+ if (best_len >= niceLength) {
+ break;
+ }
+
+ scan_end1 = window[best_end - 1];
+ scan_end = window[best_end];
+ }
+ scan = strstart;
+ } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0);
+
+ matchLen = Math.Min(best_len, lookahead);
+ return matchLen >= MIN_MATCH;
+ }
+
+ public void SetDictionary(byte[] buffer, int offset, int length)
+ {
+ if (DeflaterConstants.DEBUGGING && strstart != 1) {
+ throw new InvalidOperationException("strstart not 1");
+ }
+ adler.Update(buffer, offset, length);
+ if (length < MIN_MATCH) {
+ return;
+ }
+ if (length > MAX_DIST) {
+ offset += length - MAX_DIST;
+ length = MAX_DIST;
+ }
+
+ System.Array.Copy(buffer, offset, window, strstart, length);
+
+ UpdateHash();
+ --length;
+ while (--length > 0) {
+ InsertString();
+ strstart++;
+ }
+ strstart += 2;
+ blockStart = strstart;
+ }
+
+ private bool DeflateStored(bool flush, bool finish)
+ {
+ if (!flush && lookahead == 0) {
+ return false;
+ }
+
+ strstart += lookahead;
+ lookahead = 0;
+
+ int storedLen = strstart - blockStart;
+
+ if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) || /* Block is full */
+ (blockStart < WSIZE && storedLen >= MAX_DIST) || /* Block may move out of window */
+ flush) {
+ bool lastBlock = finish;
+ if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) {
+ storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
+ lastBlock = false;
+ }
+
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("storedBlock["+storedLen+","+lastBlock+"]");
+// }
+
+ huffman.FlushStoredBlock(window, blockStart, storedLen, lastBlock);
+ blockStart += storedLen;
+ return !lastBlock;
+ }
+ return true;
+ }
+
+ private bool DeflateFast(bool flush, bool finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush) {
+ return false;
+ }
+
+ while (lookahead >= MIN_LOOKAHEAD || flush) {
+ if (lookahead == 0) {
+ /* We are flushing everything */
+ huffman.FlushBlock(window, blockStart, strstart - blockStart, finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ SlideWindow();
+ }
+
+ int hashHead;
+ if (lookahead >= MIN_MATCH &&
+ (hashHead = InsertString()) != 0 &&
+ strategy != DeflateStrategy.HuffmanOnly &&
+ strstart - hashHead <= MAX_DIST &&
+ FindLongestMatch(hashHead)) {
+ /* longestMatch sets matchStart and matchLen */
+// if (DeflaterConstants.DEBUGGING) {
+// for (int i = 0 ; i < matchLen; i++) {
+// if (window[strstart+i] != window[matchStart + i]) {
+// throw new Exception();
+// }
+// }
+// }
+
+ huffman.TallyDist(strstart - matchStart, matchLen);
+
+ lookahead -= matchLen;
+ if (matchLen <= max_lazy && lookahead >= MIN_MATCH) {
+ while (--matchLen > 0) {
+ ++strstart;
+ InsertString();
+ }
+ ++strstart;
+ } else {
+ strstart += matchLen;
+ if (lookahead >= MIN_MATCH - 1) {
+ UpdateHash();
+ }
+ }
+ matchLen = MIN_MATCH - 1;
+ continue;
+ } else {
+ /* No match found */
+ huffman.TallyLit(window[strstart] & 0xff);
+ ++strstart;
+ --lookahead;
+ }
+
+ if (huffman.IsFull()) {
+ bool lastBlock = finish && lookahead == 0;
+ huffman.FlushBlock(window, blockStart, strstart - blockStart,
+ lastBlock);
+ blockStart = strstart;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ private bool DeflateSlow(bool flush, bool finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush) {
+ return false;
+ }
+
+ while (lookahead >= MIN_LOOKAHEAD || flush) {
+ if (lookahead == 0) {
+ if (prevAvailable) {
+ huffman.TallyLit(window[strstart-1] & 0xff);
+ }
+ prevAvailable = false;
+
+ /* We are flushing everything */
+ if (DeflaterConstants.DEBUGGING && !flush) {
+ throw new Exception("Not flushing, but no lookahead");
+ }
+ huffman.FlushBlock(window, blockStart, strstart - blockStart,
+ finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ SlideWindow();
+ }
+
+ int prevMatch = matchStart;
+ int prevLen = matchLen;
+ if (lookahead >= MIN_MATCH) {
+ int hashHead = InsertString();
+ if (strategy != DeflateStrategy.HuffmanOnly && hashHead != 0 && strstart - hashHead <= MAX_DIST && FindLongestMatch(hashHead))
+ {
+ /* longestMatch sets matchStart and matchLen */
+
+ /* Discard match if too small and too far away */
+ if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == MIN_MATCH && strstart - matchStart > TOO_FAR))) {
+ matchLen = MIN_MATCH - 1;
+ }
+ }
+ }
+
+ /* previous match was better */
+ if (prevLen >= MIN_MATCH && matchLen <= prevLen) {
+// if (DeflaterConstants.DEBUGGING) {
+// for (int i = 0 ; i < matchLen; i++) {
+// if (window[strstart-1+i] != window[prevMatch + i])
+// throw new Exception();
+// }
+// }
+ huffman.TallyDist(strstart - 1 - prevMatch, prevLen);
+ prevLen -= 2;
+ do {
+ strstart++;
+ lookahead--;
+ if (lookahead >= MIN_MATCH) {
+ InsertString();
+ }
+ } while (--prevLen > 0);
+ strstart ++;
+ lookahead--;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ } else {
+ if (prevAvailable) {
+ huffman.TallyLit(window[strstart-1] & 0xff);
+ }
+ prevAvailable = true;
+ strstart++;
+ lookahead--;
+ }
+
+ if (huffman.IsFull()) {
+ int len = strstart - blockStart;
+ if (prevAvailable) {
+ len--;
+ }
+ bool lastBlock = (finish && lookahead == 0 && !prevAvailable);
+ huffman.FlushBlock(window, blockStart, len, lastBlock);
+ blockStart += len;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ public bool Deflate(bool flush, bool finish)
+ {
+ bool progress;
+ do {
+ FillWindow();
+ bool canFlush = flush && inputOff == inputEnd;
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("window: ["+blockStart+","+strstart+","
+// +lookahead+"], "+comprFunc+","+canFlush);
+// }
+ switch (comprFunc) {
+ case DEFLATE_STORED:
+ progress = DeflateStored(canFlush, finish);
+ break;
+ case DEFLATE_FAST:
+ progress = DeflateFast(canFlush, finish);
+ break;
+ case DEFLATE_SLOW:
+ progress = DeflateSlow(canFlush, finish);
+ break;
+ default:
+ throw new InvalidOperationException("unknown comprFunc");
+ }
+ } while (pending.IsFlushed && progress); /* repeat while we have no pending output and progress was made */
+ return progress;
+ }
+
+ public void SetInput(byte[] buf, int off, int len)
+ {
+ if (inputOff < inputEnd) {
+ throw new InvalidOperationException("Old input was not completely processed");
+ }
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.Length) {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ inputBuf = buf;
+ inputOff = off;
+ inputEnd = end;
+ }
+
+ public bool NeedsInput()
+ {
+ return inputEnd == inputOff;
+ }
+ }
+}
diff --git a/PNGNet/Compression/DeflaterHuffman.cs b/PNGNet/Compression/DeflaterHuffman.cs
new file mode 100644
index 0000000..ad1e288
--- /dev/null
+++ b/PNGNet/Compression/DeflaterHuffman.cs
@@ -0,0 +1,778 @@
+// DeflaterHuffman.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ ///
+ /// This is the DeflaterHuffman class.
+ ///
+ /// This class is not thread safe. This is inherent in the API, due
+ /// to the split of deflate and setInput.
+ ///
+ /// author of the original java version : Jochen Hoenicke
+ ///
+ public class DeflaterHuffman
+ {
+ private static int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
+ private static int LITERAL_NUM = 286;
+ private static int DIST_NUM = 30;
+ private static int BITLEN_NUM = 19;
+ private static int REP_3_6 = 16;
+ private static int REP_3_10 = 17;
+ private static int REP_11_138 = 18;
+ private static int EOF_SYMBOL = 256;
+ private static int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ private static byte[] bit4Reverse = {
+ 0,
+ 8,
+ 4,
+ 12,
+ 2,
+ 10,
+ 6,
+ 14,
+ 1,
+ 9,
+ 5,
+ 13,
+ 3,
+ 11,
+ 7,
+ 15
+ };
+
+
+ public class Tree
+ {
+ public short[] freqs;
+ public short[] codes;
+ public byte[] length;
+ public int[] bl_counts;
+ public int minNumCodes, numCodes;
+ public int maxLength;
+ DeflaterHuffman dh;
+
+ public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength)
+ {
+ this.dh = dh;
+ this.minNumCodes = minCodes;
+ this.maxLength = maxLength;
+ freqs = new short[elems];
+ bl_counts = new int[maxLength];
+ }
+
+ public void Reset()
+ {
+ for (int i = 0; i < freqs.Length; i++) {
+ freqs[i] = 0;
+ }
+ codes = null;
+ length = null;
+ }
+
+ public void WriteSymbol(int code)
+ {
+// if (DeflaterConstants.DEBUGGING) {
+// freqs[code]--;
+// // Console.Write("writeSymbol("+freqs.length+","+code+"): ");
+// }
+ dh.pending.WriteBits(codes[code] & 0xffff, length[code]);
+ }
+
+ public void CheckEmpty()
+ {
+ bool empty = true;
+ for (int i = 0; i < freqs.Length; i++) {
+ if (freqs[i] != 0) {
+ Console.WriteLine("freqs["+i+"] == "+freqs[i]);
+ empty = false;
+ }
+ }
+ if (!empty) {
+ throw new Exception();
+ }
+ Console.WriteLine("checkEmpty suceeded!");
+ }
+
+ public void SetStaticCodes(short[] stCodes, byte[] stLength)
+ {
+ codes = stCodes;
+ length = stLength;
+ }
+
+ public void BuildCodes()
+ {
+ int numSymbols = freqs.Length;
+ int[] nextCode = new int[maxLength];
+ int code = 0;
+ codes = new short[freqs.Length];
+
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("buildCodes: "+freqs.Length);
+// }
+
+ for (int bits = 0; bits < maxLength; bits++) {
+ nextCode[bits] = code;
+ code += bl_counts[bits] << (15 - bits);
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("bits: "+(bits+1)+" count: "+bl_counts[bits]
+// +" nextCode: "+code); // HACK : Integer.toHexString(
+// }
+ }
+ if (DeflaterConstants.DEBUGGING && code != 65536) {
+ throw new Exception("Inconsistent bl_counts!");
+ }
+
+ for (int i=0; i < numCodes; i++) {
+ int bits = length[i];
+ if (bits > 0) {
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+")," // HACK : Integer.toHexString(
+// +bits);
+// }
+ codes[i] = BitReverse(nextCode[bits-1]);
+ nextCode[bits-1] += 1 << (16 - bits);
+ }
+ }
+ }
+
+ private void BuildLength(int[] childs)
+ {
+ this.length = new byte [freqs.Length];
+ int numNodes = childs.Length / 2;
+ int numLeafs = (numNodes + 1) / 2;
+ int overflow = 0;
+
+ for (int i = 0; i < maxLength; i++) {
+ bl_counts[i] = 0;
+ }
+
+ /* First calculate optimal bit lengths */
+ int[] lengths = new int[numNodes];
+ lengths[numNodes-1] = 0;
+
+ for (int i = numNodes - 1; i >= 0; i--) {
+ if (childs[2*i+1] != -1) {
+ int bitLength = lengths[i] + 1;
+ if (bitLength > maxLength) {
+ bitLength = maxLength;
+ overflow++;
+ }
+ lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;
+ } else {
+ /* A leaf node */
+ int bitLength = lengths[i];
+ bl_counts[bitLength - 1]++;
+ this.length[childs[2*i]] = (byte) lengths[i];
+ }
+ }
+
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("Tree "+freqs.Length+" lengths:");
+// for (int i=0; i < numLeafs; i++) {
+// Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+// + " len: "+length[childs[2*i]]);
+// }
+// }
+
+ if (overflow == 0) {
+ return;
+ }
+
+ int incrBitLen = maxLength - 1;
+ do {
+ /* Find the first bit length which could increase: */
+ while (bl_counts[--incrBitLen] == 0)
+ ;
+
+ /* Move this node one down and remove a corresponding
+ * amount of overflow nodes.
+ */
+ do {
+ bl_counts[incrBitLen]--;
+ bl_counts[++incrBitLen]++;
+ overflow -= 1 << (maxLength - 1 - incrBitLen);
+ } while (overflow > 0 && incrBitLen < maxLength - 1);
+ } while (overflow > 0);
+
+ /* We may have overshot above. Move some nodes from maxLength to
+ * maxLength-1 in that case.
+ */
+ bl_counts[maxLength-1] += overflow;
+ bl_counts[maxLength-2] -= overflow;
+
+ /* Now recompute all bit lengths, scanning in increasing
+ * frequency. It is simpler to reconstruct all lengths instead of
+ * fixing only the wrong ones. This idea is taken from 'ar'
+ * written by Haruhiko Okumura.
+ *
+ * The nodes were inserted with decreasing frequency into the childs
+ * array.
+ */
+ int nodePtr = 2 * numLeafs;
+ for (int bits = maxLength; bits != 0; bits--) {
+ int n = bl_counts[bits-1];
+ while (n > 0) {
+ int childPtr = 2*childs[nodePtr++];
+ if (childs[childPtr + 1] == -1) {
+ /* We found another leaf */
+ length[childs[childPtr]] = (byte) bits;
+ n--;
+ }
+ }
+ }
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("*** After overflow elimination. ***");
+// for (int i=0; i < numLeafs; i++) {
+// Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+// + " len: "+length[childs[2*i]]);
+// }
+// }
+ }
+
+ public void BuildTree()
+ {
+ int numSymbols = freqs.Length;
+
+ /* heap is a priority queue, sorted by frequency, least frequent
+ * nodes first. The heap is a binary tree, with the property, that
+ * the parent node is smaller than both child nodes. This assures
+ * that the smallest node is the first parent.
+ *
+ * The binary tree is encoded in an array: 0 is root node and
+ * the nodes 2*n+1, 2*n+2 are the child nodes of node n.
+ */
+ int[] heap = new int[numSymbols];
+ int heapLen = 0;
+ int maxCode = 0;
+ for (int n = 0; n < numSymbols; n++) {
+ int freq = freqs[n];
+ if (freq != 0) {
+ /* Insert n into heap */
+ int pos = heapLen++;
+ int ppos;
+ while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) {
+ heap[pos] = heap[ppos];
+ pos = ppos;
+ }
+ heap[pos] = n;
+
+ maxCode = n;
+ }
+ }
+
+ /* We could encode a single literal with 0 bits but then we
+ * don't see the literals. Therefore we force at least two
+ * literals to avoid this case. We don't care about order in
+ * this case, both literals get a 1 bit code.
+ */
+ while (heapLen < 2) {
+ int node = maxCode < 2 ? ++maxCode : 0;
+ heap[heapLen++] = node;
+ }
+
+ numCodes = Math.Max(maxCode + 1, minNumCodes);
+
+ int numLeafs = heapLen;
+ int[] childs = new int[4*heapLen - 2];
+ int[] values = new int[2*heapLen - 1];
+ int numNodes = numLeafs;
+ for (int i = 0; i < heapLen; i++) {
+ int node = heap[i];
+ childs[2*i] = node;
+ childs[2*i+1] = -1;
+ values[i] = freqs[node] << 8;
+ heap[i] = i;
+ }
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ do {
+ int first = heap[0];
+ int last = heap[--heapLen];
+
+ /* Propagate the hole to the leafs of the heap */
+ int ppos = 0;
+ int path = 1;
+ while (path < heapLen) {
+ if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {
+ path++;
+ }
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = path * 2 + 1;
+ }
+
+ /* Now propagate the last element down along path. Normally
+ * it shouldn't go too deep.
+ */
+ int lastVal = values[last];
+ while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {
+ heap[path] = heap[ppos];
+ }
+ heap[path] = last;
+
+
+ int second = heap[0];
+
+ /* Create a new node father of first and second */
+ last = numNodes++;
+ childs[2*last] = first;
+ childs[2*last+1] = second;
+ int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff);
+ values[last] = lastVal = values[first] + values[second] - mindepth + 1;
+
+ /* Again, propagate the hole to the leafs */
+ ppos = 0;
+ path = 1;
+ while (path < heapLen) {
+ if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {
+ path++;
+ }
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = ppos * 2 + 1;
+ }
+
+ /* Now propagate the new element down along path */
+ while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {
+ heap[path] = heap[ppos];
+ }
+ heap[path] = last;
+ } while (heapLen > 1);
+
+ if (heap[0] != childs.Length / 2 - 1) {
+ throw new Exception("Weird!");
+ }
+ BuildLength(childs);
+ }
+
+ public int GetEncodedLength()
+ {
+ int len = 0;
+ for (int i = 0; i < freqs.Length; i++) {
+ len += freqs[i] * length[i];
+ }
+ return len;
+ }
+
+ public void CalcBLFreq(Tree blTree)
+ {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes) {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0) {
+ max_count = 138;
+ min_count = 3;
+ } else {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen) {
+ blTree.freqs[nextlen]++;
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i]) {
+ i++;
+ if (++count >= max_count) {
+ break;
+ }
+ }
+
+ if (count < min_count) {
+ blTree.freqs[curlen] += (short)count;
+ } else if (curlen != 0) {
+ blTree.freqs[REP_3_6]++;
+ } else if (count <= 10) {
+ blTree.freqs[REP_3_10]++;
+ } else {
+ blTree.freqs[REP_11_138]++;
+ }
+ }
+ }
+
+ public void WriteTree(Tree blTree)
+ {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes) {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0) {
+ max_count = 138;
+ min_count = 3;
+ } else {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen) {
+ blTree.WriteSymbol(nextlen);
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i]) {
+ i++;
+ if (++count >= max_count) {
+ break;
+ }
+ }
+
+ if (count < min_count) {
+ while (count-- > 0) {
+ blTree.WriteSymbol(curlen);
+ }
+ }
+ else if (curlen != 0) {
+ blTree.WriteSymbol(REP_3_6);
+ dh.pending.WriteBits(count - 3, 2);
+ } else if (count <= 10) {
+ blTree.WriteSymbol(REP_3_10);
+ dh.pending.WriteBits(count - 3, 3);
+ } else {
+ blTree.WriteSymbol(REP_11_138);
+ dh.pending.WriteBits(count - 11, 7);
+ }
+ }
+ }
+ }
+
+ public DeflaterPending pending;
+ private Tree literalTree, distTree, blTree;
+
+ private short[] d_buf;
+ private byte[] l_buf;
+ private int last_lit;
+ private int extra_bits;
+
+ private static short[] staticLCodes;
+ private static byte[] staticLLength;
+ private static short[] staticDCodes;
+ private static byte[] staticDLength;
+
+ ///
+ /// Reverse the bits of a 16 bit value.
+ ///
+ public static short BitReverse(int value)
+ {
+ return (short) (bit4Reverse[value & 0xF] << 12
+ | bit4Reverse[(value >> 4) & 0xF] << 8
+ | bit4Reverse[(value >> 8) & 0xF] << 4
+ | bit4Reverse[value >> 12]);
+ }
+
+
+ static DeflaterHuffman()
+ {
+ /* See RFC 1951 3.2.6 */
+ /* Literal codes */
+ staticLCodes = new short[LITERAL_NUM];
+ staticLLength = new byte[LITERAL_NUM];
+ int i = 0;
+ while (i < 144) {
+ staticLCodes[i] = BitReverse((0x030 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+ while (i < 256) {
+ staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
+ staticLLength[i++] = 9;
+ }
+ while (i < 280) {
+ staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
+ staticLLength[i++] = 7;
+ }
+ while (i < LITERAL_NUM) {
+ staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+
+ /* Distant codes */
+ staticDCodes = new short[DIST_NUM];
+ staticDLength = new byte[DIST_NUM];
+ for (i = 0; i < DIST_NUM; i++) {
+ staticDCodes[i] = BitReverse(i << 11);
+ staticDLength[i] = 5;
+ }
+ }
+
+ public DeflaterHuffman(DeflaterPending pending)
+ {
+ this.pending = pending;
+
+ literalTree = new Tree(this, LITERAL_NUM, 257, 15);
+ distTree = new Tree(this, DIST_NUM, 1, 15);
+ blTree = new Tree(this, BITLEN_NUM, 4, 7);
+
+ d_buf = new short[BUFSIZE];
+ l_buf = new byte [BUFSIZE];
+ }
+
+ public void Reset()
+ {
+ last_lit = 0;
+ extra_bits = 0;
+ literalTree.Reset();
+ distTree.Reset();
+ blTree.Reset();
+ }
+
+ private int L_code(int len)
+ {
+ if (len == 255) {
+ return 285;
+ }
+
+ int code = 257;
+ while (len >= 8) {
+ code += 4;
+ len >>= 1;
+ }
+ return code + len;
+ }
+
+ private int D_code(int distance)
+ {
+ int code = 0;
+ while (distance >= 4) {
+ code += 2;
+ distance >>= 1;
+ }
+ return code + distance;
+ }
+
+ public void SendAllTrees(int blTreeCodes)
+ {
+ blTree.BuildCodes();
+ literalTree.BuildCodes();
+ distTree.BuildCodes();
+ pending.WriteBits(literalTree.numCodes - 257, 5);
+ pending.WriteBits(distTree.numCodes - 1, 5);
+ pending.WriteBits(blTreeCodes - 4, 4);
+ for (int rank = 0; rank < blTreeCodes; rank++) {
+ pending.WriteBits(blTree.length[BL_ORDER[rank]], 3);
+ }
+ literalTree.WriteTree(blTree);
+ distTree.WriteTree(blTree);
+// if (DeflaterConstants.DEBUGGING) {
+// blTree.CheckEmpty();
+// }
+ }
+
+ public void CompressBlock()
+ {
+ for (int i = 0; i < last_lit; i++) {
+ int litlen = l_buf[i] & 0xff;
+ int dist = d_buf[i];
+ if (dist-- != 0) {
+// if (DeflaterConstants.DEBUGGING) {
+// Console.Write("["+(dist+1)+","+(litlen+3)+"]: ");
+// }
+
+ int lc = L_code(litlen);
+ literalTree.WriteSymbol(lc);
+
+ int bits = (lc - 261) / 4;
+ if (bits > 0 && bits <= 5) {
+ pending.WriteBits(litlen & ((1 << bits) - 1), bits);
+ }
+
+ int dc = D_code(dist);
+ distTree.WriteSymbol(dc);
+
+ bits = dc / 2 - 1;
+ if (bits > 0) {
+ pending.WriteBits(dist & ((1 << bits) - 1), bits);
+ }
+ } else {
+// if (DeflaterConstants.DEBUGGING) {
+// if (litlen > 32 && litlen < 127) {
+// Console.Write("("+(char)litlen+"): ");
+// } else {
+// Console.Write("{"+litlen+"}: ");
+// }
+// }
+ literalTree.WriteSymbol(litlen);
+ }
+ }
+// if (DeflaterConstants.DEBUGGING) {
+// Console.Write("EOF: ");
+// }
+ literalTree.WriteSymbol(EOF_SYMBOL);
+// if (DeflaterConstants.DEBUGGING) {
+// literalTree.CheckEmpty();
+// distTree.CheckEmpty();
+// }
+ }
+
+ public void FlushStoredBlock(byte[] stored, int stored_offset, int stored_len, bool lastBlock)
+ {
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("Flushing stored block "+ stored_len);
+// }
+ pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1)
+ + (lastBlock ? 1 : 0), 3);
+ pending.AlignToByte();
+ pending.WriteShort(stored_len);
+ pending.WriteShort(~stored_len);
+ pending.WriteBlock(stored, stored_offset, stored_len);
+ Reset();
+ }
+
+ public void FlushBlock(byte[] stored, int stored_offset, int stored_len, bool lastBlock)
+ {
+ literalTree.freqs[EOF_SYMBOL]++;
+
+ /* Build trees */
+ literalTree.BuildTree();
+ distTree.BuildTree();
+
+ /* Calculate bitlen frequency */
+ literalTree.CalcBLFreq(blTree);
+ distTree.CalcBLFreq(blTree);
+
+ /* Build bitlen tree */
+ blTree.BuildTree();
+
+ int blTreeCodes = 4;
+ for (int i = 18; i > blTreeCodes; i--) {
+ if (blTree.length[BL_ORDER[i]] > 0) {
+ blTreeCodes = i+1;
+ }
+ }
+ int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() +
+ literalTree.GetEncodedLength() + distTree.GetEncodedLength() +
+ extra_bits;
+
+ int static_len = extra_bits;
+ for (int i = 0; i < LITERAL_NUM; i++) {
+ static_len += literalTree.freqs[i] * staticLLength[i];
+ }
+ for (int i = 0; i < DIST_NUM; i++) {
+ static_len += distTree.freqs[i] * staticDLength[i];
+ }
+ if (opt_len >= static_len) {
+ /* Force static trees */
+ opt_len = static_len;
+ }
+
+ if (stored_offset >= 0 && stored_len+4 < opt_len >> 3) {
+ /* Store Block */
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("Storing, since " + stored_len + " < " + opt_len
+// + " <= " + static_len);
+// }
+ FlushStoredBlock(stored, stored_offset, stored_len, lastBlock);
+ } else if (opt_len == static_len) {
+ /* Encode with static tree */
+ pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3);
+ literalTree.SetStaticCodes(staticLCodes, staticLLength);
+ distTree.SetStaticCodes(staticDCodes, staticDLength);
+ CompressBlock();
+ Reset();
+ } else {
+ /* Encode with dynamic tree */
+ pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3);
+ SendAllTrees(blTreeCodes);
+ CompressBlock();
+ Reset();
+ }
+ }
+
+ public bool IsFull()
+ {
+ return last_lit + 16 >= BUFSIZE; // HACK: This was == 'last_lit == BUFSIZE', but errors occured with DeflateFast
+ }
+
+ public bool TallyLit(int lit)
+ {
+// if (DeflaterConstants.DEBUGGING) {
+// if (lit > 32 && lit < 127) {
+// Console.WriteLine("("+(char)lit+")");
+// } else {
+// Console.WriteLine("{"+lit+"}");
+// }
+// }
+ d_buf[last_lit] = 0;
+ l_buf[last_lit++] = (byte)lit;
+ literalTree.freqs[lit]++;
+ return IsFull();
+ }
+
+ public bool TallyDist(int dist, int len)
+ {
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("["+dist+","+len+"]");
+// }
+
+ d_buf[last_lit] = (short)dist;
+ l_buf[last_lit++] = (byte)(len - 3);
+
+ int lc = L_code(len - 3);
+ literalTree.freqs[lc]++;
+ if (lc >= 265 && lc < 285) {
+ extra_bits += (lc - 261) / 4;
+ }
+
+ int dc = D_code(dist - 1);
+ distTree.freqs[dc]++;
+ if (dc >= 4) {
+ extra_bits += dc / 2 - 1;
+ }
+ return IsFull();
+ }
+ }
+}
diff --git a/PNGNet/Compression/DeflaterPending.cs b/PNGNet/Compression/DeflaterPending.cs
new file mode 100644
index 0000000..f50b3b7
--- /dev/null
+++ b/PNGNet/Compression/DeflaterPending.cs
@@ -0,0 +1,51 @@
+// DeflaterPending.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ ///
+ /// This class stores the pending output of the Deflater.
+ ///
+ /// author of the original java version : Jochen Hoenicke
+ ///
+ public class DeflaterPending : PendingBuffer
+ {
+ public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE)
+ {
+ }
+ }
+}
diff --git a/PNGNet/Compression/Inflater.cs b/PNGNet/Compression/Inflater.cs
new file mode 100644
index 0000000..cb6c841
--- /dev/null
+++ b/PNGNet/Compression/Inflater.cs
@@ -0,0 +1,778 @@
+// Inflater.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+using ICSharpCode.SharpZipLib.Checksums;
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ ///
+ /// Inflater is used to decompress data that has been compressed according
+ /// to the "deflate" standard described in rfc1950.
+ ///
+ /// The usage is as following. First you have to set some input with
+ /// setInput()
, then inflate() it. If inflate doesn't
+ /// inflate any bytes there may be three reasons:
+ ///
+ /// - needsInput() returns true because the input buffer is empty.
+ /// You have to provide more input with
setInput()
.
+ /// NOTE: needsInput() also returns true when, the stream is finished.
+ ///
+ /// - needsDictionary() returns true, you have to provide a preset
+ /// dictionary with
setDictionary()
.
+ /// - finished() returns true, the inflater has finished.
+ ///
+ /// Once the first output byte is produced, a dictionary will not be
+ /// needed at a later stage.
+ ///
+ /// author of the original java version : John Leuner, Jochen Hoenicke
+ ///
+ public class Inflater
+ {
+ ///
+ /// Copy lengths for literal codes 257..285
+ ///
+ private static int[] CPLENS = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
+ };
+
+ ///
+ /// Extra bits for literal codes 257..285
+ ///
+ private static int[] CPLEXT = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
+ };
+
+ ///
+ /// Copy offsets for distance codes 0..29
+ ///
+ private static int[] CPDIST = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577
+ };
+
+ ///
+ /// Extra bits for distance codes
+ ///
+ private static int[] CPDEXT = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13
+ };
+
+ ///
+ /// This are the state in which the inflater can be.
+ ///
+ private const int DECODE_HEADER = 0;
+ private const int DECODE_DICT = 1;
+ private const int DECODE_BLOCKS = 2;
+ private const int DECODE_STORED_LEN1 = 3;
+ private const int DECODE_STORED_LEN2 = 4;
+ private const int DECODE_STORED = 5;
+ private const int DECODE_DYN_HEADER = 6;
+ private const int DECODE_HUFFMAN = 7;
+ private const int DECODE_HUFFMAN_LENBITS = 8;
+ private const int DECODE_HUFFMAN_DIST = 9;
+ private const int DECODE_HUFFMAN_DISTBITS = 10;
+ private const int DECODE_CHKSUM = 11;
+ private const int FINISHED = 12;
+
+ ///
+ /// This variable contains the current state.
+ ///
+ private int mode;
+
+ ///
+ /// The adler checksum of the dictionary or of the decompressed
+ /// stream, as it is written in the header resp. footer of the
+ /// compressed stream.
+ /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
+ ///
+ private int readAdler;
+
+ ///
+ /// The number of bits needed to complete the current state. This
+ /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
+ /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
+ ///
+ private int neededBits;
+ private int repLength, repDist;
+ private int uncomprLen;
+
+ ///
+ /// True, if the last block flag was set in the last block of the
+ /// inflated stream. This means that the stream ends after the
+ /// current block.
+ ///
+ private bool isLastBlock;
+
+ ///
+ /// The total number of inflated bytes.
+ ///
+ private int totalOut;
+
+ ///
+ /// The total number of bytes set with setInput(). This is not the
+ /// value returned by getTotalIn(), since this also includes the
+ /// unprocessed input.
+ ///
+ private int totalIn;
+
+ ///
+ /// This variable stores the nowrap flag that was given to the constructor.
+ /// True means, that the inflated stream doesn't contain a header nor the
+ /// checksum in the footer.
+ ///
+ private bool nowrap;
+
+ private StreamManipulator input;
+ private OutputWindow outputWindow;
+ private InflaterDynHeader dynHeader;
+ private InflaterHuffmanTree litlenTree, distTree;
+ private Adler32 adler;
+
+ ///
+ /// Creates a new inflater.
+ ///
+ public Inflater() : this(false)
+ {
+ }
+
+ ///
+ /// Creates a new inflater.
+ ///
+ ///
+ /// true if no header and checksum field appears in the
+ /// stream. This is used for GZIPed input. For compatibility with
+ /// Sun JDK you should provide one byte of input more than needed in
+ /// this case.
+ ///
+ public Inflater(bool nowrap)
+ {
+ this.nowrap = nowrap;
+ this.adler = new Adler32();
+ input = new StreamManipulator();
+ outputWindow = new OutputWindow();
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ }
+
+ ///
+ /// Resets the inflater so that a new stream can be decompressed. All
+ /// pending input and output will be discarded.
+ ///
+ public void Reset()
+ {
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ totalIn = totalOut = 0;
+ input.Reset();
+ outputWindow.Reset();
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ isLastBlock = false;
+ adler.Reset();
+ }
+
+ ///
+ /// Decodes the deflate header.
+ ///
+ ///
+ /// false if more input is needed.
+ ///
+ ///
+ /// if header is invalid.
+ ///
+ private bool DecodeHeader()
+ {
+ int header = input.PeekBits(16);
+ if (header < 0) {
+ return false;
+ }
+ input.DropBits(16);
+ /* The header is written in "wrong" byte order */
+ header = ((header << 8) | (header >> 8)) & 0xffff;
+ if (header % 31 != 0) {
+ throw new FormatException("Header checksum illegal");
+ }
+
+ if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) {
+ throw new FormatException("Compression Method unknown");
+ }
+
+ /* Maximum size of the backwards window in bits.
+ * We currently ignore this, but we could use it to make the
+ * inflater window more space efficient. On the other hand the
+ * full window (15 bits) is needed most times, anyway.
+ int max_wbits = ((header & 0x7000) >> 12) + 8;
+ */
+
+ if ((header & 0x0020) == 0) { // Dictionary flag?
+ mode = DECODE_BLOCKS;
+ } else {
+ mode = DECODE_DICT;
+ neededBits = 32;
+ }
+ return true;
+ }
+
+ ///
+ /// Decodes the dictionary checksum after the deflate header.
+ ///
+ ///
+ /// false if more input is needed.
+ ///
+ private bool DecodeDict()
+ {
+ while (neededBits > 0) {
+ int dictByte = input.PeekBits(8);
+ if (dictByte < 0) {
+ return false;
+ }
+ input.DropBits(8);
+ readAdler = (readAdler << 8) | dictByte;
+ neededBits -= 8;
+ }
+ return false;
+ }
+
+ ///
+ /// Decodes the huffman encoded symbols in the input stream.
+ ///
+ ///
+ /// false if more input is needed, true if output window is
+ /// full or the current block ends.
+ ///
+ ///
+ /// if deflated stream is invalid.
+ ///
+ private bool DecodeHuffman()
+ {
+ int free = outputWindow.GetFreeSpace();
+ while (free >= 258) {
+ int symbol;
+ switch (mode) {
+ case DECODE_HUFFMAN:
+ /* This is the inner loop so it is optimized a bit */
+ while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) {
+ outputWindow.Write(symbol);
+ if (--free < 258) {
+ return true;
+ }
+ }
+ if (symbol < 257) {
+ if (symbol < 0) {
+ return false;
+ } else {
+ /* symbol == 256: end of block */
+ distTree = null;
+ litlenTree = null;
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ }
+
+ try {
+ repLength = CPLENS[symbol - 257];
+ neededBits = CPLEXT[symbol - 257];
+ } catch (Exception) {
+ throw new FormatException("Illegal rep length code");
+ }
+ goto case DECODE_HUFFMAN_LENBITS;/* fall through */
+ case DECODE_HUFFMAN_LENBITS:
+ if (neededBits > 0) {
+ mode = DECODE_HUFFMAN_LENBITS;
+ int i = input.PeekBits(neededBits);
+ if (i < 0) {
+ return false;
+ }
+ input.DropBits(neededBits);
+ repLength += i;
+ }
+ mode = DECODE_HUFFMAN_DIST;
+ goto case DECODE_HUFFMAN_DIST;/* fall through */
+ case DECODE_HUFFMAN_DIST:
+ symbol = distTree.GetSymbol(input);
+ if (symbol < 0) {
+ return false;
+ }
+ try {
+ repDist = CPDIST[symbol];
+ neededBits = CPDEXT[symbol];
+ } catch (Exception) {
+ throw new FormatException("Illegal rep dist code");
+ }
+
+ goto case DECODE_HUFFMAN_DISTBITS;/* fall through */
+ case DECODE_HUFFMAN_DISTBITS:
+ if (neededBits > 0) {
+ mode = DECODE_HUFFMAN_DISTBITS;
+ int i = input.PeekBits(neededBits);
+ if (i < 0) {
+ return false;
+ }
+ input.DropBits(neededBits);
+ repDist += i;
+ }
+ outputWindow.Repeat(repLength, repDist);
+ free -= repLength;
+ mode = DECODE_HUFFMAN;
+ break;
+ default:
+ throw new FormatException();
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Decodes the adler checksum after the deflate stream.
+ ///
+ ///
+ /// false if more input is needed.
+ ///
+ ///
+ /// DataFormatException, if checksum doesn't match.
+ ///
+ private bool DecodeChksum()
+ {
+ while (neededBits > 0) {
+ int chkByte = input.PeekBits(8);
+ if (chkByte < 0) {
+ return false;
+ }
+ input.DropBits(8);
+ readAdler = (readAdler << 8) | chkByte;
+ neededBits -= 8;
+ }
+ if ((int) adler.Value != readAdler) {
+ throw new FormatException("Adler chksum doesn't match: "
+ + (int)adler.Value
+ + " vs. " + readAdler);
+ }
+ mode = FINISHED;
+ return false;
+ }
+
+ ///
+ /// Decodes the deflated stream.
+ ///
+ ///
+ /// false if more input is needed, or if finished.
+ ///
+ ///
+ /// DataFormatException, if deflated stream is invalid.
+ ///
+ private bool Decode()
+ {
+ switch (mode) {
+ case DECODE_HEADER:
+ return DecodeHeader();
+ case DECODE_DICT:
+ return DecodeDict();
+ case DECODE_CHKSUM:
+ return DecodeChksum();
+
+ case DECODE_BLOCKS:
+ if (isLastBlock) {
+ if (nowrap) {
+ mode = FINISHED;
+ return false;
+ } else {
+ input.SkipToByteBoundary();
+ neededBits = 32;
+ mode = DECODE_CHKSUM;
+ return true;
+ }
+ }
+
+ int type = input.PeekBits(3);
+ if (type < 0) {
+ return false;
+ }
+ input.DropBits(3);
+
+ if ((type & 1) != 0) {
+ isLastBlock = true;
+ }
+ switch (type >> 1) {
+ case DeflaterConstants.STORED_BLOCK:
+ input.SkipToByteBoundary();
+ mode = DECODE_STORED_LEN1;
+ break;
+ case DeflaterConstants.STATIC_TREES:
+ litlenTree = InflaterHuffmanTree.defLitLenTree;
+ distTree = InflaterHuffmanTree.defDistTree;
+ mode = DECODE_HUFFMAN;
+ break;
+ case DeflaterConstants.DYN_TREES:
+ dynHeader = new InflaterDynHeader();
+ mode = DECODE_DYN_HEADER;
+ break;
+ default:
+ throw new FormatException("Unknown block type "+type);
+ }
+ return true;
+
+ case DECODE_STORED_LEN1: {
+ if ((uncomprLen = input.PeekBits(16)) < 0) {
+ return false;
+ }
+ input.DropBits(16);
+ mode = DECODE_STORED_LEN2;
+ }
+ goto case DECODE_STORED_LEN2; /* fall through */
+ case DECODE_STORED_LEN2: {
+ int nlen = input.PeekBits(16);
+ if (nlen < 0) {
+ return false;
+ }
+ input.DropBits(16);
+ if (nlen != (uncomprLen ^ 0xffff)) {
+ throw new FormatException("broken uncompressed block");
+ }
+ mode = DECODE_STORED;
+ }
+ goto case DECODE_STORED;/* fall through */
+ case DECODE_STORED: {
+ int more = outputWindow.CopyStored(input, uncomprLen);
+ uncomprLen -= more;
+ if (uncomprLen == 0) {
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ return !input.IsNeedingInput;
+ }
+
+ case DECODE_DYN_HEADER:
+ if (!dynHeader.Decode(input)) {
+ return false;
+ }
+
+ litlenTree = dynHeader.BuildLitLenTree();
+ distTree = dynHeader.BuildDistTree();
+ mode = DECODE_HUFFMAN;
+ goto case DECODE_HUFFMAN; /* fall through */
+ case DECODE_HUFFMAN:
+ case DECODE_HUFFMAN_LENBITS:
+ case DECODE_HUFFMAN_DIST:
+ case DECODE_HUFFMAN_DISTBITS:
+ return DecodeHuffman();
+ case FINISHED:
+ return false;
+ default:
+ throw new FormatException();
+ }
+ }
+
+ ///
+ /// Sets the preset dictionary. This should only be called, if
+ /// needsDictionary() returns true and it should set the same
+ /// dictionary, that was used for deflating. The getAdler()
+ /// function returns the checksum of the dictionary needed.
+ ///
+ ///
+ /// the dictionary.
+ ///
+ ///
+ /// if no dictionary is needed.
+ ///
+ ///
+ /// if the dictionary checksum is wrong.
+ ///
+ public void SetDictionary(byte[] buffer)
+ {
+ SetDictionary(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Sets the preset dictionary. This should only be called, if
+ /// needsDictionary() returns true and it should set the same
+ /// dictionary, that was used for deflating. The getAdler()
+ /// function returns the checksum of the dictionary needed.
+ ///
+ ///
+ /// the dictionary.
+ ///
+ ///
+ /// the offset into buffer where the dictionary starts.
+ ///
+ ///
+ /// the length of the dictionary.
+ ///
+ ///
+ /// if no dictionary is needed.
+ ///
+ ///
+ /// if the dictionary checksum is wrong.
+ ///
+ ///
+ /// if the off and/or len are wrong.
+ ///
+ public void SetDictionary(byte[] buffer, int off, int len)
+ {
+ if (!IsNeedingDictionary) {
+ throw new InvalidOperationException();
+ }
+
+ adler.Update(buffer, off, len);
+ if ((int) adler.Value != readAdler) {
+ throw new ArgumentException("Wrong adler checksum");
+ }
+ adler.Reset();
+ outputWindow.CopyDict(buffer, off, len);
+ mode = DECODE_BLOCKS;
+ }
+
+ ///
+ /// Sets the input. This should only be called, if needsInput()
+ /// returns true.
+ ///
+ ///
+ /// the input.
+ ///
+ ///
+ /// if no input is needed.
+ ///
+ public void SetInput(byte[] buf)
+ {
+ SetInput(buf, 0, buf.Length);
+ }
+
+ ///
+ /// Sets the input. This should only be called, if needsInput()
+ /// returns true.
+ ///
+ ///
+ /// the input.
+ ///
+ ///
+ /// the offset into buffer where the input starts.
+ ///
+ ///
+ /// the length of the input.
+ ///
+ ///
+ /// if no input is needed.
+ ///
+ ///
+ /// if the off and/or len are wrong.
+ ///
+ public void SetInput(byte[] buf, int off, int len)
+ {
+ input.SetInput(buf, off, len);
+ totalIn += len;
+ }
+
+ ///
+ /// Inflates the compressed stream to the output buffer. If this
+ /// returns 0, you should check, whether needsDictionary(),
+ /// needsInput() or finished() returns true, to determine why no
+ /// further output is produced.
+ ///
+ ///
+ /// the output buffer.
+ ///
+ ///
+ /// the number of bytes written to the buffer, 0 if no further
+ /// output can be produced.
+ ///
+ ///
+ /// if buf has length 0.
+ ///
+ ///
+ /// if deflated stream is invalid.
+ ///
+ public int Inflate(byte[] buf)
+ {
+ return Inflate(buf, 0, buf.Length);
+ }
+
+ ///
+ /// Inflates the compressed stream to the output buffer. If this
+ /// returns 0, you should check, whether needsDictionary(),
+ /// needsInput() or finished() returns true, to determine why no
+ /// further output is produced.
+ ///
+ ///
+ /// the output buffer.
+ ///
+ ///
+ /// the offset into buffer where the output should start.
+ ///
+ ///
+ /// the maximum length of the output.
+ ///
+ ///
+ /// the number of bytes written to the buffer, 0 if no further output can be produced.
+ ///
+ ///
+ /// if len is <= 0.
+ ///
+ ///
+ /// if the off and/or len are wrong.
+ ///
+ ///
+ /// if deflated stream is invalid.
+ ///
+ public int Inflate(byte[] buf, int off, int len)
+ {
+ if (len < 0) {
+ throw new ArgumentOutOfRangeException("len < 0");
+ }
+ // Special case: len may be zero
+ if (len == 0) {
+ return 0;
+ }
+/* // Check for correct buff, off, len triple
+ if (off < 0 || off + len >= buf.Length) {
+ throw new ArgumentException("off/len outside buf bounds");
+ }*/
+ int count = 0;
+ int more;
+ do {
+ if (mode != DECODE_CHKSUM) {
+ /* Don't give away any output, if we are waiting for the
+ * checksum in the input stream.
+ *
+ * With this trick we have always:
+ * needsInput() and not finished()
+ * implies more output can be produced.
+ */
+ more = outputWindow.CopyOutput(buf, off, len);
+ adler.Update(buf, off, more);
+ off += more;
+ count += more;
+ totalOut += more;
+ len -= more;
+ if (len == 0) {
+ return count;
+ }
+ }
+ } while (Decode() || (outputWindow.GetAvailable() > 0 &&
+ mode != DECODE_CHKSUM));
+ return count;
+ }
+
+ ///
+ /// Returns true, if the input buffer is empty.
+ /// You should then call setInput().
+ /// NOTE: This method also returns true when the stream is finished.
+ ///
+ public bool IsNeedingInput {
+ get {
+ return input.IsNeedingInput;
+ }
+ }
+
+ ///
+ /// Returns true, if a preset dictionary is needed to inflate the input.
+ ///
+ public bool IsNeedingDictionary {
+ get {
+ return mode == DECODE_DICT && neededBits == 0;
+ }
+ }
+
+ ///
+ /// Returns true, if the inflater has finished. This means, that no
+ /// input is needed and no output can be produced.
+ ///
+ public bool IsFinished {
+ get {
+ return mode == FINISHED && outputWindow.GetAvailable() == 0;
+ }
+ }
+
+ ///
+ /// Gets the adler checksum. This is either the checksum of all
+ /// uncompressed bytes returned by inflate(), or if needsDictionary()
+ /// returns true (and thus no output was yet produced) this is the
+ /// adler checksum of the expected dictionary.
+ ///
+ ///
+ /// the adler checksum.
+ ///
+ public int Adler {
+ get {
+ return IsNeedingDictionary ? readAdler : (int) adler.Value;
+ }
+ }
+
+ ///
+ /// Gets the total number of output bytes returned by inflate().
+ ///
+ ///
+ /// the total number of output bytes.
+ ///
+ public int TotalOut {
+ get {
+ return totalOut;
+ }
+ }
+
+ ///
+ /// Gets the total number of processed compressed input bytes.
+ ///
+ ///
+ /// the total number of bytes of processed input bytes.
+ ///
+ public int TotalIn {
+ get {
+ return totalIn - RemainingInput;
+ }
+ }
+
+ ///
+ /// Gets the number of unprocessed input. Useful, if the end of the
+ /// stream is reached and you want to further process the bytes after
+ /// the deflate stream.
+ ///
+ ///
+ /// the number of bytes of the input which were not processed.
+ ///
+ public int RemainingInput {
+ get {
+ return input.AvailableBytes;
+ }
+ }
+ }
+}
diff --git a/PNGNet/Compression/InflaterDynHeader.cs b/PNGNet/Compression/InflaterDynHeader.cs
new file mode 100644
index 0000000..94a7da3
--- /dev/null
+++ b/PNGNet/Compression/InflaterDynHeader.cs
@@ -0,0 +1,203 @@
+// InflaterDynHeader.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ class InflaterDynHeader
+ {
+ const int LNUM = 0;
+ const int DNUM = 1;
+ const int BLNUM = 2;
+ const int BLLENS = 3;
+ const int LENS = 4;
+ const int REPS = 5;
+
+ static readonly int[] repMin = { 3, 3, 11 };
+ static readonly int[] repBits = { 2, 3, 7 };
+
+ byte[] blLens;
+ byte[] litdistLens;
+
+ InflaterHuffmanTree blTree;
+
+ int mode;
+ int lnum, dnum, blnum, num;
+ int repSymbol;
+ byte lastLen;
+ int ptr;
+
+ static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ public InflaterDynHeader()
+ {
+ }
+
+ public bool Decode(StreamManipulator input)
+ {
+ decode_loop:
+ for (;;) {
+ switch (mode) {
+ case LNUM:
+ lnum = input.PeekBits(5);
+ if (lnum < 0) {
+ return false;
+ }
+ lnum += 257;
+ input.DropBits(5);
+ // System.err.println("LNUM: "+lnum);
+ mode = DNUM;
+ goto case DNUM; // fall through
+ case DNUM:
+ dnum = input.PeekBits(5);
+ if (dnum < 0) {
+ return false;
+ }
+ dnum++;
+ input.DropBits(5);
+ // System.err.println("DNUM: "+dnum);
+ num = lnum+dnum;
+ litdistLens = new byte[num];
+ mode = BLNUM;
+ goto case BLNUM; // fall through
+ case BLNUM:
+ blnum = input.PeekBits(4);
+ if (blnum < 0) {
+ return false;
+ }
+ blnum += 4;
+ input.DropBits(4);
+ blLens = new byte[19];
+ ptr = 0;
+ // System.err.println("BLNUM: "+blnum);
+ mode = BLLENS;
+ goto case BLLENS; // fall through
+ case BLLENS:
+ while (ptr < blnum) {
+ int len = input.PeekBits(3);
+ if (len < 0) {
+ return false;
+ }
+ input.DropBits(3);
+ // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
+ blLens[BL_ORDER[ptr]] = (byte) len;
+ ptr++;
+ }
+ blTree = new InflaterHuffmanTree(blLens);
+ blLens = null;
+ ptr = 0;
+ mode = LENS;
+ goto case LENS; // fall through
+ case LENS: {
+ int symbol;
+ while (((symbol = blTree.GetSymbol(input)) & ~15) == 0) {
+ /* Normal case: symbol in [0..15] */
+
+ // System.err.println("litdistLens["+ptr+"]: "+symbol);
+ litdistLens[ptr++] = lastLen = (byte)symbol;
+
+ if (ptr == num) {
+ /* Finished */
+ return true;
+ }
+ }
+
+ /* need more input ? */
+ if (symbol < 0)
+ return false;
+
+ /* otherwise repeat code */
+ if (symbol >= 17) {
+ /* repeat zero */
+ // System.err.println("repeating zero");
+ lastLen = 0;
+ } else {
+ if (ptr == 0) {
+ throw new Exception();
+ }
+ }
+ repSymbol = symbol-16;
+ }
+ mode = REPS;
+ goto case REPS; // fall through
+ case REPS:
+ {
+ int bits = repBits[repSymbol];
+ int count = input.PeekBits(bits);
+ if (count < 0) {
+ return false;
+ }
+ input.DropBits(bits);
+ count += repMin[repSymbol];
+ // System.err.println("litdistLens repeated: "+count);
+
+ if (ptr + count > num) {
+ throw new Exception();
+ }
+ while (count-- > 0) {
+ litdistLens[ptr++] = lastLen;
+ }
+
+ if (ptr == num) {
+ /* Finished */
+ return true;
+ }
+ }
+ mode = LENS;
+ goto decode_loop;
+ }
+ }
+ }
+
+ public InflaterHuffmanTree BuildLitLenTree()
+ {
+ byte[] litlenLens = new byte[lnum];
+ Array.Copy(litdistLens, 0, litlenLens, 0, lnum);
+ return new InflaterHuffmanTree(litlenLens);
+ }
+
+ public InflaterHuffmanTree BuildDistTree()
+ {
+ byte[] distLens = new byte[dnum];
+ Array.Copy(litdistLens, lnum, distLens, 0, dnum);
+ return new InflaterHuffmanTree(distLens);
+ }
+ }
+}
diff --git a/PNGNet/Compression/InflaterHuffmanTree.cs b/PNGNet/Compression/InflaterHuffmanTree.cs
new file mode 100644
index 0000000..dd4bbf0
--- /dev/null
+++ b/PNGNet/Compression/InflaterHuffmanTree.cs
@@ -0,0 +1,208 @@
+// InflaterHuffmanTree.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ public class InflaterHuffmanTree
+ {
+ private static int MAX_BITLEN = 15;
+ private short[] tree;
+
+ public static InflaterHuffmanTree defLitLenTree, defDistTree;
+
+ static InflaterHuffmanTree()
+ {
+ try {
+ byte[] codeLengths = new byte[288];
+ int i = 0;
+ while (i < 144) {
+ codeLengths[i++] = 8;
+ }
+ while (i < 256) {
+ codeLengths[i++] = 9;
+ }
+ while (i < 280) {
+ codeLengths[i++] = 7;
+ }
+ while (i < 288) {
+ codeLengths[i++] = 8;
+ }
+ defLitLenTree = new InflaterHuffmanTree(codeLengths);
+
+ codeLengths = new byte[32];
+ i = 0;
+ while (i < 32) {
+ codeLengths[i++] = 5;
+ }
+ defDistTree = new InflaterHuffmanTree(codeLengths);
+ } catch (Exception) {
+ throw new ApplicationException("InflaterHuffmanTree: static tree length illegal");
+ }
+ }
+
+ ///
+ /// Constructs a Huffman tree from the array of code lengths.
+ ///
+ ///
+ /// the array of code lengths
+ ///
+ public InflaterHuffmanTree(byte[] codeLengths)
+ {
+ BuildTree(codeLengths);
+ }
+
+ private void BuildTree(byte[] codeLengths)
+ {
+ int[] blCount = new int[MAX_BITLEN + 1];
+ int[] nextCode = new int[MAX_BITLEN + 1];
+
+ for (int i = 0; i < codeLengths.Length; i++) {
+ int bits = codeLengths[i];
+ if (bits > 0)
+ blCount[bits]++;
+ }
+
+ int code = 0;
+ int treeSize = 512;
+ for (int bits = 1; bits <= MAX_BITLEN; bits++) {
+ nextCode[bits] = code;
+ code += blCount[bits] << (16 - bits);
+ if (bits >= 10) {
+ /* We need an extra table for bit lengths >= 10. */
+ int start = nextCode[bits] & 0x1ff80;
+ int end = code & 0x1ff80;
+ treeSize += (end - start) >> (16 - bits);
+ }
+ }
+ if (code != 65536) {
+ throw new Exception("Code lengths don't add up properly.");
+ }
+ /* Now create and fill the extra tables from longest to shortest
+ * bit len. This way the sub trees will be aligned.
+ */
+ tree = new short[treeSize];
+ int treePtr = 512;
+ for (int bits = MAX_BITLEN; bits >= 10; bits--) {
+ int end = code & 0x1ff80;
+ code -= blCount[bits] << (16 - bits);
+ int start = code & 0x1ff80;
+ for (int i = start; i < end; i += 1 << 7) {
+ tree[DeflaterHuffman.BitReverse(i)] = (short) ((-treePtr << 4) | bits);
+ treePtr += 1 << (bits-9);
+ }
+ }
+
+ for (int i = 0; i < codeLengths.Length; i++) {
+ int bits = codeLengths[i];
+ if (bits == 0) {
+ continue;
+ }
+ code = nextCode[bits];
+ int revcode = DeflaterHuffman.BitReverse(code);
+ if (bits <= 9) {
+ do {
+ tree[revcode] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ } while (revcode < 512);
+ } else {
+ int subTree = tree[revcode & 511];
+ int treeLen = 1 << (subTree & 15);
+ subTree = -(subTree >> 4);
+ do {
+ tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ } while (revcode < treeLen);
+ }
+ nextCode[bits] = code + (1 << (16 - bits));
+ }
+
+ }
+
+ ///
+ /// Reads the next symbol from input. The symbol is encoded using the
+ /// huffman tree.
+ ///
+ ///
+ /// input the input source.
+ ///
+ ///
+ /// the next symbol, or -1 if not enough input is available.
+ ///
+ public int GetSymbol(StreamManipulator input)
+ {
+ int lookahead, symbol;
+ if ((lookahead = input.PeekBits(9)) >= 0) {
+ if ((symbol = tree[lookahead]) >= 0) {
+ input.DropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ int subtree = -(symbol >> 4);
+ int bitlen = symbol & 15;
+ if ((lookahead = input.PeekBits(bitlen)) >= 0) {
+ symbol = tree[subtree | (lookahead >> 9)];
+ input.DropBits(symbol & 15);
+ return symbol >> 4;
+ } else {
+ int bits = input.AvailableBits;
+ lookahead = input.PeekBits(bits);
+ symbol = tree[subtree | (lookahead >> 9)];
+ if ((symbol & 15) <= bits) {
+ input.DropBits(symbol & 15);
+ return symbol >> 4;
+ } else {
+ return -1;
+ }
+ }
+ } else {
+ int bits = input.AvailableBits;
+ lookahead = input.PeekBits(bits);
+ symbol = tree[lookahead];
+ if (symbol >= 0 && (symbol & 15) <= bits) {
+ input.DropBits(symbol & 15);
+ return symbol >> 4;
+ } else {
+ return -1;
+ }
+ }
+ }
+ }
+}
+
diff --git a/PNGNet/Compression/PendingBuffer.cs b/PNGNet/Compression/PendingBuffer.cs
new file mode 100644
index 0000000..f823fbe
--- /dev/null
+++ b/PNGNet/Compression/PendingBuffer.cs
@@ -0,0 +1,208 @@
+// PendingBuffer.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression {
+
+ ///
+ /// This class is general purpose class for writing data to a buffer.
+ ///
+ /// It allows you to write bits as well as bytes
+ /// Based on DeflaterPending.java
+ ///
+ /// author of the original java version : Jochen Hoenicke
+ ///
+ public class PendingBuffer
+ {
+ protected byte[] buf;
+ int start;
+ int end;
+
+ uint bits;
+ int bitCount;
+
+ public PendingBuffer() : this( 4096 )
+ {
+
+ }
+
+ public PendingBuffer(int bufsize)
+ {
+ buf = new byte[bufsize];
+ }
+
+ public void Reset()
+ {
+ start = end = bitCount = 0;
+ }
+
+ public void WriteByte(int b)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new Exception();
+ buf[end++] = (byte) b;
+ }
+
+ public void WriteShort(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ }
+
+ public void WriteInt(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) (s >> 16);
+ buf[end++] = (byte) (s >> 24);
+ }
+
+ public void WriteBlock(byte[] block, int offset, int len)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+ System.Array.Copy(block, offset, buf, end, len);
+ end += len;
+ }
+
+ public int BitCount {
+ get {
+ return bitCount;
+ }
+ }
+
+ public void AlignToByte()
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+ if (bitCount > 0) {
+ buf[end++] = (byte) bits;
+ if (bitCount > 8) {
+ buf[end++] = (byte) (bits >> 8);
+ }
+ }
+ bits = 0;
+ bitCount = 0;
+ }
+
+ public void WriteBits(int b, int count)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+// if (DeflaterConstants.DEBUGGING) {
+// Console.WriteLine("writeBits("+b+","+count+")");
+// }
+ bits |= (uint)(b << bitCount);
+ bitCount += count;
+ if (bitCount >= 16) {
+ buf[end++] = (byte) bits;
+ buf[end++] = (byte) (bits >> 8);
+ bits >>= 16;
+ bitCount -= 16;
+ }
+ }
+
+ public void WriteShortMSB(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0) {
+ throw new Exception();
+ }
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) s;
+ }
+
+ public bool IsFlushed {
+ get {
+ return end == 0;
+ }
+ }
+
+ ///
+ /// Flushes the pending buffer into the given output array. If the
+ /// output array is to small, only a partial flush is done.
+ ///
+ ///
+ /// the output array;
+ ///
+ ///
+ /// the offset into output array;
+ ///
+ ///
+ /// length the maximum number of bytes to store;
+ ///
+ ///
+ /// IndexOutOfBoundsException if offset or length are invalid.
+ ///
+ public int Flush(byte[] output, int offset, int length)
+ {
+ if (bitCount >= 8) {
+ buf[end++] = (byte) bits;
+ bits >>= 8;
+ bitCount -= 8;
+ }
+ if (length > end - start) {
+ length = end - start;
+ System.Array.Copy(buf, start, output, offset, length);
+ start = 0;
+ end = 0;
+ } else {
+ System.Array.Copy(buf, start, output, offset, length);
+ start += length;
+ }
+ return length;
+ }
+
+ public byte[] ToByteArray()
+ {
+ byte[] ret = new byte[ end - start ];
+ System.Array.Copy(buf, start, ret, 0, ret.Length);
+ start = 0;
+ end = 0;
+ return ret;
+ }
+ }
+}
diff --git a/PNGNet/Compression/Streams/DeflaterOutputStream.cs b/PNGNet/Compression/Streams/DeflaterOutputStream.cs
new file mode 100644
index 0000000..acaad07
--- /dev/null
+++ b/PNGNet/Compression/Streams/DeflaterOutputStream.cs
@@ -0,0 +1,296 @@
+// DeflaterOutputStream.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+using System.IO;
+
+using ICSharpCode.SharpZipLib.Zip.Compression;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams {
+
+ ///
+ /// This is a special FilterOutputStream deflating the bytes that are
+ /// written through it. It uses the Deflater for deflating.
+ ///
+ /// authors of the original java version : Tom Tromey, Jochen Hoenicke
+ ///
+ public class DeflaterOutputStream : Stream
+ {
+ ///
+ /// This buffer is used temporarily to retrieve the bytes from the
+ /// deflater and write them to the underlying output stream.
+ ///
+ protected byte[] buf;
+
+ ///
+ /// The deflater which is used to deflate the stream.
+ ///
+ protected Deflater def;
+
+ ///
+ /// base stream the deflater depends on.
+ ///
+ protected Stream baseOutputStream;
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanRead {
+ get {
+ return baseOutputStream.CanRead;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanSeek {
+ get {
+ return baseOutputStream.CanSeek;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanWrite {
+ get {
+ return baseOutputStream.CanWrite;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Length {
+ get {
+ return baseOutputStream.Length;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Position {
+ get {
+ return baseOutputStream.Position;
+ }
+ set {
+ baseOutputStream.Position = value;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return baseOutputStream.Seek(offset, origin);
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override void SetLength(long val)
+ {
+ baseOutputStream.SetLength(val);
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override int ReadByte()
+ {
+ return baseOutputStream.ReadByte();
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override int Read(byte[] b, int off, int len)
+ {
+ return baseOutputStream.Read(b, off, len);
+ }
+
+
+ ///
+ /// Deflates everything in the def's input buffers. This will call
+ /// def.deflate()
until all bytes from the input buffers
+ /// are processed.
+ ///
+ protected void deflate()
+ {
+ while (!def.IsNeedingInput) {
+ int len = def.Deflate(buf, 0, buf.Length);
+
+ // System.err.println("DOS deflated " + len + " baseOutputStream of " + buf.length);
+ if (len <= 0) {
+ break;
+ }
+ baseOutputStream.Write(buf, 0, len);
+ }
+
+ if (!def.IsNeedingInput) {
+ throw new ApplicationException("Can't deflate all input?");
+ }
+ }
+
+ ///
+ /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
+ ///
+ ///
+ /// the output stream where deflated output should be written.
+ ///
+ public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512)
+ {
+
+ }
+
+ ///
+ /// Creates a new DeflaterOutputStream with the given Deflater and
+ /// default buffer size.
+ ///
+ ///
+ /// the output stream where deflated output should be written.
+ ///
+ ///
+ /// the underlying deflater.
+ ///
+ public DeflaterOutputStream(Stream baseOutputStream, Deflater defl) :this(baseOutputStream, defl, 512)
+ {
+ }
+
+ ///
+ /// Creates a new DeflaterOutputStream with the given Deflater and
+ /// buffer size.
+ ///
+ ///
+ /// the output stream where deflated output should be written.
+ ///
+ ///
+ /// the underlying deflater.
+ ///
+ ///
+ /// the buffer size.
+ ///
+ ///
+ /// if bufsize isn't positive.
+ ///
+ public DeflaterOutputStream(Stream baseOutputStream, Deflater defl, int bufsize)
+ {
+ this.baseOutputStream = baseOutputStream;
+ if (bufsize <= 0) {
+ throw new InvalidOperationException("bufsize <= 0");
+ }
+ buf = new byte[bufsize];
+ def = defl;
+ }
+
+ ///
+ /// Flushes the stream by calling flush() on the deflater and then
+ /// on the underlying stream. This ensures that all bytes are
+ /// flushed.
+ ///
+ public override void Flush()
+ {
+ def.Flush();
+ deflate();
+ baseOutputStream.Flush();
+ }
+
+ ///
+ /// Finishes the stream by calling finish() on the deflater.
+ ///
+ public virtual void Finish()
+ {
+ def.Finish();
+ while (!def.IsFinished) {
+ int len = def.Deflate(buf, 0, buf.Length);
+ if (len <= 0) {
+ break;
+ }
+ baseOutputStream.Write(buf, 0, len);
+ }
+ if (!def.IsFinished) {
+ throw new ApplicationException("Can't deflate all input?");
+ }
+ baseOutputStream.Flush();
+ }
+
+ ///
+ /// Calls finish () and closes the stream.
+ ///
+ public override void Close()
+ {
+ Finish();
+ baseOutputStream.Close();
+ }
+
+ ///
+ /// Writes a single byte to the compressed output stream.
+ ///
+ ///
+ /// the byte value.
+ ///
+ public override void WriteByte(byte bval)
+ {
+ byte[] b = new byte[1];
+ b[0] = (byte) bval;
+ Write(b, 0, 1);
+ }
+
+ ///
+ /// Writes a len bytes from an array to the compressed stream.
+ ///
+ ///
+ /// the byte array.
+ ///
+ ///
+ /// the offset into the byte array where to start.
+ ///
+ ///
+ /// the number of bytes to write.
+ ///
+ public override void Write(byte[] buf, int off, int len)
+ {
+ // System.err.println("DOS with off " + off + " and len " + len);
+ def.SetInput(buf, off, len);
+ deflate();
+ }
+
+ }
+}
diff --git a/PNGNet/Compression/Streams/InflaterInputStream.cs b/PNGNet/Compression/Streams/InflaterInputStream.cs
new file mode 100644
index 0000000..9e963a1
--- /dev/null
+++ b/PNGNet/Compression/Streams/InflaterInputStream.cs
@@ -0,0 +1,333 @@
+// InflaterInputStream.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+using System.IO;
+
+using ICSharpCode.SharpZipLib.Zip.Compression;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams {
+
+ ///
+ /// This filter stream is used to decompress data compressed baseInputStream the "deflate"
+ /// format. The "deflate" format is described baseInputStream RFC 1951.
+ ///
+ /// This stream may form the basis for other decompression filters, such
+ /// as the GzipInputStream
.
+ ///
+ /// author of the original java version : John Leuner
+ ///
+ public class InflaterInputStream : Stream
+ {
+ //Variables
+
+ ///
+ /// Decompressor for this filter
+ ///
+ protected Inflater inf;
+
+
+ ///
+ /// Byte array used as a buffer
+ ///
+ protected byte[] buf;
+
+ ///
+ /// Size of buffer
+ ///
+ protected int len;
+
+ //We just use this if we are decoding one byte at a time with the read() call
+ private byte[] onebytebuffer = new byte[1];
+
+ ///
+ /// base stream the inflater depends on.
+ ///
+ protected Stream baseInputStream;
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanRead {
+ get {
+ return baseInputStream.CanRead;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanSeek {
+ get {
+ return baseInputStream.CanSeek;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override bool CanWrite {
+ get {
+ return baseInputStream.CanWrite;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Length {
+ get {
+ return len;
+ }
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Position {
+ get {
+ return baseInputStream.Position;
+ }
+ set {
+ baseInputStream.Position = value;
+ }
+ }
+
+ ///
+ /// Flushes the baseInputStream
+ ///
+ public override void Flush()
+ {
+ baseInputStream.Flush();
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return baseInputStream.Seek(offset, origin);
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override void SetLength(long val)
+ {
+ baseInputStream.SetLength(val);
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override void Write(byte[] array, int offset, int count)
+ {
+ baseInputStream.Write(array, offset, count);
+ }
+
+ ///
+ /// I needed to implement the abstract member.
+ ///
+ public override void WriteByte(byte val)
+ {
+ baseInputStream.WriteByte(val);
+ }
+
+ //Constructors
+
+ ///
+ /// Create an InflaterInputStream with the default decompresseor
+ /// and a default buffer size.
+ ///
+ ///
+ /// the InputStream to read bytes from
+ ///
+ public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096)
+ {
+
+ }
+
+ ///
+ /// Create an InflaterInputStream with the specified decompresseor
+ /// and a default buffer size.
+ ///
+ ///
+ /// the InputStream to read bytes from
+ ///
+ ///
+ /// the decompressor used to decompress data read from baseInputStream
+ ///
+ public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096)
+ {
+
+ }
+
+ ///
+ /// Create an InflaterInputStream with the specified decompresseor
+ /// and a specified buffer size.
+ ///
+ ///
+ /// the InputStream to read bytes from
+ ///
+ ///
+ /// the decompressor used to decompress data read from baseInputStream
+ ///
+ ///
+ /// size of the buffer to use
+ ///
+ public InflaterInputStream(Stream baseInputStream, Inflater inf, int size)
+ {
+ this.baseInputStream = baseInputStream;
+ this.inf = inf;
+ try {
+ this.len = (int)baseInputStream.Length;
+ } catch (Exception) { // the stream may not support .Length
+ this.len = 0;
+ }
+
+ if (size <= 0) {
+ throw new ArgumentOutOfRangeException("size <= 0");
+ }
+
+ buf = new byte[size]; //Create the buffer
+ }
+
+ //Methods
+
+ ///
+ /// Returns 0 once the end of the stream (EOF) has been reached.
+ /// Otherwise returns 1.
+ ///
+ public virtual int Available {
+ get {
+ return inf.IsFinished ? 0 : 1;
+ }
+ }
+
+ ///
+ /// Closes the input stream
+ ///
+ public override void Close()
+ {
+ baseInputStream.Close();
+ }
+
+ ///
+ /// Fills the buffer with more data to decompress.
+ ///
+ protected void Fill()
+ {
+ len = baseInputStream.Read(buf, 0, buf.Length);
+
+ if (len < 0) {
+ throw new ApplicationException("Deflated stream ends early.");
+ }
+ inf.SetInput(buf, 0, len);
+ }
+
+ ///
+ /// Reads one byte of decompressed data.
+ ///
+ /// The byte is baseInputStream the lower 8 bits of the int.
+ ///
+ public override int ReadByte()
+ {
+ int nread = Read(onebytebuffer, 0, 1); //read one byte
+ if (nread > 0) {
+ return onebytebuffer[0] & 0xff;
+ }
+ return -1;
+ }
+
+ ///
+ /// Decompresses data into the byte array
+ ///
+ ///
+ /// the array to read and decompress data into
+ ///
+ ///
+ /// the offset indicating where the data should be placed
+ ///
+ ///
+ /// the number of bytes to decompress
+ ///
+ public override int Read(byte[] b, int off, int len)
+ {
+ for (;;) {
+ int count;
+ try {
+ count = inf.Inflate(b, off, len);
+ } catch (Exception e) {
+ throw new ZipException(e.ToString());
+ }
+
+ if (count > 0) {
+ return count;
+ }
+
+ if (inf.IsNeedingDictionary) {
+ throw new ZipException("Need a dictionary");
+ } else if (inf.IsFinished) {
+ return 0;
+ } else if (inf.IsNeedingInput) {
+ Fill();
+ } else {
+ throw new InvalidOperationException("Don't know what to do");
+ }
+ }
+ }
+
+ ///
+ /// Skip specified number of bytes of uncompressed data
+ ///
+ ///
+ /// number of bytes to skip
+ ///
+ public long Skip(long n)
+ {
+ if (n < 0) {
+ throw new ArgumentOutOfRangeException("n");
+ }
+ int len = 2048;
+ if (n < len) {
+ len = (int) n;
+ }
+ byte[] tmp = new byte[len];
+ return (long)baseInputStream.Read(tmp, 0, tmp.Length);
+ }
+ }
+}
diff --git a/PNGNet/Compression/Streams/OutputWindow.cs b/PNGNet/Compression/Streams/OutputWindow.cs
new file mode 100644
index 0000000..eef6b77
--- /dev/null
+++ b/PNGNet/Compression/Streams/OutputWindow.cs
@@ -0,0 +1,176 @@
+// OutputWindow.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams {
+
+ ///
+ /// Contains the output from the Inflation process.
+ /// We need to have a window so that we can refer backwards into the output stream
+ /// to repeat stuff.
+ ///
+ /// author of the original java version : John Leuner
+ ///
+ public class OutputWindow
+ {
+ private static int WINDOW_SIZE = 1 << 15;
+ private static int WINDOW_MASK = WINDOW_SIZE - 1;
+
+ private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
+ private int window_end = 0;
+ private int window_filled = 0;
+
+ public void Write(int abyte)
+ {
+ if (window_filled++ == WINDOW_SIZE) {
+ throw new InvalidOperationException("Window full");
+ }
+ window[window_end++] = (byte) abyte;
+ window_end &= WINDOW_MASK;
+ }
+
+
+ private void SlowRepeat(int rep_start, int len, int dist)
+ {
+ while (len-- > 0) {
+ window[window_end++] = window[rep_start++];
+ window_end &= WINDOW_MASK;
+ rep_start &= WINDOW_MASK;
+ }
+ }
+
+ public void Repeat(int len, int dist)
+ {
+ if ((window_filled += len) > WINDOW_SIZE) {
+ throw new InvalidOperationException("Window full");
+ }
+
+ int rep_start = (window_end - dist) & WINDOW_MASK;
+ int border = WINDOW_SIZE - len;
+ if (rep_start <= border && window_end < border) {
+ if (len <= dist) {
+ System.Array.Copy(window, rep_start, window, window_end, len);
+ window_end += len;
+ } else {
+ /* We have to copy manually, since the repeat pattern overlaps.
+ */
+ while (len-- > 0) {
+ window[window_end++] = window[rep_start++];
+ }
+ }
+ } else {
+ SlowRepeat(rep_start, len, dist);
+ }
+ }
+
+ public int CopyStored(StreamManipulator input, int len)
+ {
+ len = Math.Min(Math.Min(len, WINDOW_SIZE - window_filled), input.AvailableBytes);
+ int copied;
+
+ int tailLen = WINDOW_SIZE - window_end;
+ if (len > tailLen) {
+ copied = input.CopyBytes(window, window_end, tailLen);
+ if (copied == tailLen) {
+ copied += input.CopyBytes(window, 0, len - tailLen);
+ }
+ } else {
+ copied = input.CopyBytes(window, window_end, len);
+ }
+
+ window_end = (window_end + copied) & WINDOW_MASK;
+ window_filled += copied;
+ return copied;
+ }
+
+ public void CopyDict(byte[] dict, int offset, int len)
+ {
+ if (window_filled > 0) {
+ throw new InvalidOperationException();
+ }
+
+ if (len > WINDOW_SIZE) {
+ offset += len - WINDOW_SIZE;
+ len = WINDOW_SIZE;
+ }
+ System.Array.Copy(dict, offset, window, 0, len);
+ window_end = len & WINDOW_MASK;
+ }
+
+ public int GetFreeSpace()
+ {
+ return WINDOW_SIZE - window_filled;
+ }
+
+ public int GetAvailable()
+ {
+ return window_filled;
+ }
+
+ public int CopyOutput(byte[] output, int offset, int len)
+ {
+ int copy_end = window_end;
+ if (len > window_filled) {
+ len = window_filled;
+ } else {
+ copy_end = (window_end - window_filled + len) & WINDOW_MASK;
+ }
+
+ int copied = len;
+ int tailLen = len - copy_end;
+
+ if (tailLen > 0) {
+ System.Array.Copy(window, WINDOW_SIZE - tailLen,
+ output, offset, tailLen);
+ offset += tailLen;
+ len = copy_end;
+ }
+ System.Array.Copy(window, copy_end - len, output, offset, len);
+ window_filled -= copied;
+ if (window_filled < 0) {
+ throw new InvalidOperationException();
+ }
+ return copied;
+ }
+
+ public void Reset()
+ {
+ window_filled = window_end = 0;
+ }
+ }
+}
diff --git a/PNGNet/Compression/Streams/StreamManipulator.cs b/PNGNet/Compression/Streams/StreamManipulator.cs
new file mode 100644
index 0000000..23540ad
--- /dev/null
+++ b/PNGNet/Compression/Streams/StreamManipulator.cs
@@ -0,0 +1,244 @@
+// StreamManipulator.cs
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+using System;
+
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams {
+
+ ///
+ /// This class allows us to retrieve a specified amount of bits from
+ /// the input buffer, as well as copy big byte blocks.
+ ///
+ /// It uses an int buffer to store up to 31 bits for direct
+ /// manipulation. This guarantees that we can get at least 16 bits,
+ /// but we only need at most 15, so this is all safe.
+ ///
+ /// There are some optimizations in this class, for example, you must
+ /// never peek more then 8 bits more than needed, and you must first
+ /// peek bits before you may drop them. This is not a general purpose
+ /// class but optimized for the behaviour of the Inflater.
+ ///
+ /// authors of the original java version : John Leuner, Jochen Hoenicke
+ ///
+ public class StreamManipulator
+ {
+ private byte[] window;
+ private int window_start = 0;
+ private int window_end = 0;
+
+ private uint buffer = 0;
+ private int bits_in_buffer = 0;
+
+ ///
+ /// Get the next n bits but don't increase input pointer. n must be
+ /// less or equal 16 and if you if this call succeeds, you must drop
+ /// at least n-8 bits in the next call.
+ ///
+ ///
+ /// the value of the bits, or -1 if not enough bits available. */
+ ///
+ public int PeekBits(int n)
+ {
+ if (bits_in_buffer < n) {
+ if (window_start == window_end) {
+ return -1;
+ }
+ buffer |= (uint)((window[window_start++] & 0xff |
+ (window[window_start++] & 0xff) << 8) << bits_in_buffer);
+ bits_in_buffer += 16;
+ }
+ return (int)(buffer & ((1 << n) - 1));
+ }
+
+ ///
+ /// Drops the next n bits from the input. You should have called peekBits
+ /// with a bigger or equal n before, to make sure that enough bits are in
+ /// the bit buffer.
+ ///
+ public void DropBits(int n)
+ {
+ buffer >>= n;
+ bits_in_buffer -= n;
+ }
+
+ ///
+ /// Gets the next n bits and increases input pointer. This is equivalent
+ /// to peekBits followed by dropBits, except for correct error handling.
+ ///
+ ///
+ /// the value of the bits, or -1 if not enough bits available.
+ ///
+ public int GetBits(int n)
+ {
+ int bits = PeekBits(n);
+ if (bits >= 0) {
+ DropBits(n);
+ }
+ return bits;
+ }
+
+ ///
+ /// Gets the number of bits available in the bit buffer. This must be
+ /// only called when a previous peekBits() returned -1.
+ ///
+ ///
+ /// the number of bits available.
+ ///
+ public int AvailableBits {
+ get {
+ return bits_in_buffer;
+ }
+ }
+
+ ///
+ /// Gets the number of bytes available.
+ ///
+ ///
+ /// the number of bytes available.
+ ///
+ public int AvailableBytes {
+ get {
+ return window_end - window_start + (bits_in_buffer >> 3);
+ }
+ }
+
+ ///
+ /// Skips to the next byte boundary.
+ ///
+ public void SkipToByteBoundary()
+ {
+ buffer >>= (bits_in_buffer & 7);
+ bits_in_buffer &= ~7;
+ }
+
+ public bool IsNeedingInput {
+ get {
+ return window_start == window_end;
+ }
+ }
+
+ ///
+ /// Copies length bytes from input buffer to output buffer starting
+ /// at output[offset]. You have to make sure, that the buffer is
+ /// byte aligned. If not enough bytes are available, copies fewer
+ /// bytes.
+ ///
+ ///
+ /// the buffer.
+ ///
+ ///
+ /// the offset in the buffer.
+ ///
+ ///
+ /// the length to copy, 0 is allowed.
+ ///
+ ///
+ /// the number of bytes copied, 0 if no byte is available.
+ ///
+ public int CopyBytes(byte[] output, int offset, int length)
+ {
+ if (length < 0) {
+ throw new ArgumentOutOfRangeException("length negative");
+ }
+ if ((bits_in_buffer & 7) != 0) {
+ /* bits_in_buffer may only be 0 or 8 */
+ throw new InvalidOperationException("Bit buffer is not aligned!");
+ }
+
+ int count = 0;
+ while (bits_in_buffer > 0 && length > 0) {
+ output[offset++] = (byte) buffer;
+ buffer >>= 8;
+ bits_in_buffer -= 8;
+ length--;
+ count++;
+ }
+ if (length == 0) {
+ return count;
+ }
+
+ int avail = window_end - window_start;
+ if (length > avail) {
+ length = avail;
+ }
+ System.Array.Copy(window, window_start, output, offset, length);
+ window_start += length;
+
+ if (((window_start - window_end) & 1) != 0) {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer = (uint)(window[window_start++] & 0xff);
+ bits_in_buffer = 8;
+ }
+ return count + length;
+ }
+
+ public StreamManipulator()
+ {
+ }
+
+ public void Reset()
+ {
+ buffer = (uint)(window_start = window_end = bits_in_buffer = 0);
+ }
+
+ public void SetInput(byte[] buf, int off, int len)
+ {
+ if (window_start < window_end) {
+ throw new InvalidOperationException("Old input was not completely processed");
+ }
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.Length) {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if ((len & 1) != 0) {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer |= (uint)((buf[off++] & 0xff) << bits_in_buffer);
+ bits_in_buffer += 8;
+ }
+
+ window = buf;
+ window_start = off;
+ window_end = end;
+ }
+ }
+}
diff --git a/PNGNet/Compression/ZipException.cs b/PNGNet/Compression/ZipException.cs
new file mode 100644
index 0000000..6c82fc4
--- /dev/null
+++ b/PNGNet/Compression/ZipException.cs
@@ -0,0 +1,82 @@
+// ZipException.cs
+//
+// Copyright (C) 2001 Mike Krueger
+//
+// This file was translated from java, it was part of the GNU Classpath
+// Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library. Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+//
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module. An independent module is a module which is not derived from
+// or based on this library. If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so. If you do not wish to do so, delete this
+// exception statement from your version.
+
+// modified by wj32
+
+using System;
+
+#if !NETCF_1_0 && !NETCF_2_0
+using System.Runtime.Serialization;
+#endif
+
+namespace ICSharpCode.SharpZipLib.Zip
+{
+
+ ///
+ /// Represents exception conditions specific to Zip archive handling
+ ///
+ public class ZipException : Exception
+ {
+
+ ///
+ /// Initializes a new instance of the ZipException class.
+ ///
+ public ZipException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ZipException class with a specified error message.
+ ///
+ /// The error message that explains the reason for the exception.
+ public ZipException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initialise a new instance of ZipException.
+ ///
+ /// A message describing the error.
+ /// The exception that is the cause of the current exception.
+ public ZipException(string message, Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/PNGNet/CompressionLevel.cs b/PNGNet/CompressionLevel.cs
new file mode 100644
index 0000000..ab75866
--- /dev/null
+++ b/PNGNet/CompressionLevel.cs
@@ -0,0 +1,96 @@
+/*
+ * PNG.Net
+ *
+ * Copyright (C) 2008 wj32
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+
+namespace PNGNet
+{
+ ///
+ /// Provides methods for changing the global compression level.
+ ///
+ public sealed class CompressionLevel : IDisposable
+ {
+ #region Static Members
+
+ ///
+ /// Indicates that the compressor should store blocks uncompressed.
+ ///
+ public const int NoCompression = 0;
+
+ ///
+ /// Indicates that the compressor should compress at the fastest speed possible.
+ ///
+ public const int BestSpeed = 1;
+
+ ///
+ /// Indicates that the compressor should compress to the smallest size possible.
+ ///
+ public const int BestCompression = 9;
+
+ private static int _level = 6;
+
+ ///
+ /// Gets or sets the compression level for compressing PNG data.
+ ///
+ public static int Level
+ {
+ get { return _level; }
+ set
+ {
+ if (value < CompressionLevel.NoCompression)
+ throw new ArgumentException("Compression level must be equal to or above 0.");
+ if (value > CompressionLevel.BestCompression)
+ throw new ArgumentException("Compression level must be equal to or below 9.");
+
+ _level = value;
+ }
+ }
+
+ #endregion
+
+ #region Class
+
+ private int _oldLevel;
+
+ ///
+ /// Temporarily sets the compression level.
+ ///
+ /// The new level.
+ public CompressionLevel(int newLevel)
+ {
+ _oldLevel = CompressionLevel.Level;
+ CompressionLevel.Level = newLevel;
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Sets the compression level to the original value.
+ ///
+ public void Dispose()
+ {
+ CompressionLevel.Level = _oldLevel;
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/PNGNet/PNGBitmap.cs b/PNGNet/PNGBitmap.cs
new file mode 100644
index 0000000..10ed1be
--- /dev/null
+++ b/PNGNet/PNGBitmap.cs
@@ -0,0 +1,1130 @@
+/*
+ * PNG.Net
+ *
+ * Copyright (C) 2008 wj32
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Text;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+
+namespace PNGNet
+{
+ ///
+ /// Provides high-level methods to read and write PNG bitmaps.
+ ///
+ public sealed class PNGBitmap
+ {
+ ///
+ /// The type of filter, applied to each scanline.
+ ///
+ public enum FilterType : byte
+ {
+ None = 0,
+ Sub,
+ Up,
+ Average,
+ Paeth
+ }
+
+ #region Adam7 Sequence
+
+ /* 0_1_2_3_4_5_6_7
+ * 0|1 6 4 6 2 6 4 6
+ * 1|7 7 7 7 7 7 7 7
+ * 2|5 6 5 6 5 6 5 6
+ * 3|7 7 7 7 7 7 7 7
+ * 4|3 6 4 6 3 6 4 6
+ * 5|7 7 7 7 7 7 7 7
+ * 6|5 6 5 6 5 6 5 6
+ * 7|7 7 7 7 7 7 7 7
+ */
+ private byte[][] _adam7 = new byte[][]
+ {
+ new byte[] { 0, 5, 3, 5, 1, 5, 3, 5 },
+ new byte[] { 6, 6, 6, 6, 6, 6, 6, 6 },
+ new byte[] { 4, 5, 4, 5, 4, 5, 4, 5 },
+ new byte[] { 6, 6, 6, 6, 6, 6, 6, 6 },
+ new byte[] { 2, 5, 3, 5, 2, 5, 3, 5 },
+ new byte[] { 6, 6, 6, 6, 6, 6, 6, 6 },
+ new byte[] { 4, 5, 4, 5, 4, 5, 4, 5 },
+ new byte[] { 6, 6, 6, 6, 6, 6, 6, 6 }
+ };
+
+ int[] _adam7Pixels, _adam7Lines;
+
+ // stored as lines, x, y, x, y, ...
+ private byte[][] _adam7Lookup = new byte[][]
+ {
+ new byte[] { 1, 0, 0 }, // 1
+ new byte[] { 1, 4, 0 }, // 2
+ new byte[] { 1, 0, 4, 4, 4}, // 3
+ new byte[]
+ {
+ 2,
+ 2, 0, 6, 0,
+ 2, 4, 6, 4
+ }, // 4
+ new byte[]
+ {
+ 2,
+ 0, 2, 2, 2, 4, 2, 6, 2,
+ 0, 6, 2, 6, 4, 6, 6, 6
+ }, // 5
+ new byte[]
+ {
+ 4,
+ 1, 0, 3, 0, 5, 0, 7, 0,
+ 1, 2, 3, 2, 5, 2, 7, 2,
+ 1, 4, 3, 4, 5, 4, 7, 4,
+ 1, 6, 3, 6, 5, 6, 7, 6
+ }, // 6
+ new byte[]
+ {
+ 4,
+ 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1,
+ 0, 3, 1, 3, 2, 3, 3, 3, 4, 3, 5, 3, 6, 3, 7, 3,
+ 0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 5, 5, 6, 5, 7, 5,
+ 0, 7, 1, 7, 2, 7, 3, 7, 4, 7, 5, 7, 6, 7, 7, 7
+ } // 7
+ };
+
+ #endregion
+
+ private PNGImage _image;
+ private Bitmap _bitmap;
+
+ // state
+ int _scanlineLength;
+ int _bpp;
+
+ ///
+ /// Creates a new PNG bitmap.
+ ///
+ /// The size.
+ /// The color type.
+ /// The bit depth.
+ public PNGBitmap(Size size, ColorType ct, byte bitDepth) : this(size.Width, size.Height, ct, bitDepth) { }
+
+ ///
+ /// Creates a new PNG bitmap.
+ ///
+ /// The width.
+ /// The height.
+ /// The color type.
+ /// The bit depth.
+ public PNGBitmap(int width, int height, ColorType ct, byte bitDepth)
+ {
+ _bitmap = CreateBitmap(ct, bitDepth, width, height);
+ }
+
+ ///
+ /// Reads a PNG file from the specified stream.
+ ///
+ /// The stream to read from.
+ public PNGBitmap(Stream s)
+ {
+ _image = new PNGImage(s);
+
+ this.Read(this.Decompress());
+ }
+
+ ///
+ /// Creates a Bitmap from the given information.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private Bitmap CreateBitmap(ColorType ct, byte bitDepth, int width, int height)
+ {
+ // don't need to use the extra information for now
+
+ return new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+ }
+
+ ///
+ /// Decompresses the compressed data stored in one or more IDAT chunks.
+ ///
+ private byte[] Decompress()
+ {
+ Inflater inf = new Inflater();
+ MemoryStream decompressed = new MemoryStream();
+ MemoryStream compressed = new MemoryStream();
+ byte[] buf = new byte[1];
+
+ // decompress the image data
+ foreach (Chunk c in _image.Chunks)
+ {
+ if (c.Type == "IDAT")
+ {
+ IDATChunk idat = c as IDATChunk;
+
+ compressed.Write(idat.Data, 0, idat.Data.Length);
+
+ if (compressed.Length > 15)
+ {
+ inf.SetInput(compressed.ToArray());
+
+ while (!inf.IsNeedingInput)
+ {
+ if (inf.Inflate(buf) == -1)
+ break;
+
+ decompressed.WriteByte(buf[0]);
+ }
+
+ compressed = new MemoryStream();
+ }
+ }
+ }
+
+ inf.SetInput(compressed.ToArray());
+
+ while (!inf.IsNeedingInput)
+ {
+ if (inf.Inflate(buf) == -1)
+ break;
+
+ decompressed.WriteByte(buf[0]);
+ }
+
+ if (!inf.IsFinished)
+ throw new InvalidCompressedDataException("Inflater is not finished but there are no more IDAT chunks.");
+
+ byte[] arr = decompressed.ToArray();
+
+ decompressed.Close();
+
+ return arr;
+ }
+
+ ///
+ /// Returns the paeth predictor of the three values.
+ ///
+ /// The left value.
+ /// The above value.
+ /// The upper left value.
+ /// The closest neighbouring value.
+ private byte PaethPredictor(byte a, byte b, byte c)
+ {
+ int p = (int)a + (int)b - (int)c;
+ int pa = (int)Math.Abs((int)p - a);
+ int pb = (int)Math.Abs((int)p - b);
+ int pc = (int)Math.Abs((int)p - c);
+
+ if ((pa <= pb) && (pa <= pc))
+ return a;
+ else if (pb <= pc)
+ return b;
+ else
+ return c;
+ }
+
+ ///
+ /// Defilters a scanline.
+ ///
+ /// The raw image data.
+ /// The scanline number.
+ /// The previous defiltered line.
+ /// The defiltered data.
+ private byte[] Defilter(byte[] data, int line, byte[] priorLine)
+ {
+ FilterType ft = (FilterType)data[line * _scanlineLength];
+ byte[] lineData = new byte[_scanlineLength - 1];
+ int lds = line * _scanlineLength + 1;
+
+ switch (ft)
+ {
+ case FilterType.None:
+ // no filtering, just copy the line over, skipping the filter type byte.
+ Array.Copy(data, lds, lineData, 0, lineData.Length);
+ break;
+
+ case FilterType.Sub:
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+
+ lineData[i] = (byte)((
+ orig + rawxbpp
+ ) % 256);
+ }
+
+ break;
+
+ case FilterType.Up:
+ if (line == 0)
+ {
+ Array.Copy(data, lds, lineData, 0, lineData.Length);
+ }
+ else
+ {
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte priorx = priorLine[i];
+
+ lineData[i] = (byte)((
+ orig + priorx
+ ) % 256);
+ }
+ }
+
+ break;
+
+ case FilterType.Average:
+ if (line == 0)
+ {
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+
+ lineData[i] = (byte)((
+ orig + (int)Math.Floor((double)rawxbpp / 2)
+ ) % 256);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+ byte priorx = priorLine[i];
+
+ lineData[i] = (byte)((
+ orig + (int)Math.Floor((double)(rawxbpp + priorx) / 2)
+ ) % 256);
+ }
+ }
+
+ break;
+
+ case FilterType.Paeth:
+ if (line == 0)
+ {
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+
+ lineData[i] = (byte)((
+ orig + this.PaethPredictor(rawxbpp, 0, 0)
+ ) % 256);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < lineData.Length; i++)
+ {
+ byte orig = data[lds + i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+ byte priorx = priorLine[i];
+ byte priorxbpp = (i - _bpp < 0) ? (byte)0 : priorLine[i - _bpp];
+
+ lineData[i] = (byte)((
+ orig + this.PaethPredictor(rawxbpp, priorx, priorxbpp)
+ ) % 256);
+ }
+ }
+
+ break;
+
+ default:
+ throw new Exception(string.Format("Unknown filter type {0}.", ft));
+ }
+
+ return lineData;
+ }
+
+ ///
+ /// Filters a scanline.
+ ///
+ /// A single line of data.
+ /// The line number.
+ /// The filter type to use.
+ /// The previous unfiltered line.
+ /// The filtered bytes.
+ private byte[] Filter(byte[] data, int line, FilterType ft, byte[] priorLine)
+ {
+ byte[] lineData = new byte[_scanlineLength];
+
+ lineData[0] = (byte)ft;
+
+ switch (ft)
+ {
+ case FilterType.None:
+ // no filtering, just copy the line over, skipping the filter type byte.
+ Array.Copy(data, 0, lineData, 1, data.Length);
+ break;
+
+ case FilterType.Sub:
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : data[i - _bpp];
+
+ lineData[i + 1] = (byte)((
+ orig - rawxbpp
+ ) % 256);
+ }
+
+ break;
+
+ case FilterType.Up:
+ if (line == 0)
+ {
+ Array.Copy(data, 0, lineData, 1, data.Length);
+ }
+ else
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte priorx = priorLine[i];
+
+ lineData[i + 1] = (byte)((
+ orig - priorx
+ ) % 256);
+ }
+ }
+
+ break;
+
+ case FilterType.Average:
+ if (line == 0)
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : data[i - _bpp];
+
+ lineData[i + 1] = (byte)((
+ orig - (int)Math.Floor((double)rawxbpp / 2)
+ ) % 256);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : data[i - _bpp];
+ byte priorx = priorLine[i];
+
+ lineData[i + 1] = (byte)((
+ orig - (int)Math.Floor((double)(rawxbpp + priorx) / 2)
+ ) % 256);
+ }
+ }
+
+ break;
+
+ case FilterType.Paeth:
+ if (line == 0)
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : lineData[i - _bpp];
+
+ lineData[i + 1] = (byte)((
+ orig - this.PaethPredictor(rawxbpp, 0, 0)
+ ) % 256);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ byte orig = data[i];
+ byte rawxbpp = (i - _bpp < 0) ? (byte)0 : data[i - _bpp];
+ byte priorx = priorLine[i];
+ byte priorxbpp = (i - _bpp < 0) ? (byte)0 : priorLine[i - _bpp];
+
+ lineData[i + 1] = (byte)((
+ orig - this.PaethPredictor(rawxbpp, priorx, priorxbpp)
+ ) % 256);
+ }
+ }
+
+ break;
+
+ default:
+ throw new Exception(string.Format("Unknown filter type {0}.", ft));
+ }
+
+ return lineData;
+ }
+
+ ///
+ /// Calculates the best filter method for the given scanline by applying each filter to the
+ /// given scanline, computing the sum of each filtered byte (as a signed value), then returning
+ /// the filter which produces the lowest absolute value of the sum.
+ ///
+ /// The scanline to test.
+ /// The line number.
+ /// The previous unfiltered line.
+ ///
+ private FilterType GetBestFilterMethod(byte[] data, int line, byte[] priorLine)
+ {
+ Dictionary values = new Dictionary();
+
+ foreach (FilterType ft in Enum.GetValues(typeof(FilterType)))
+ {
+ byte[] f = this.Filter(data, line, ft, priorLine);
+ long sum = 0;
+
+ foreach (byte b in f)
+ {
+ if (b > 127)
+ sum -= 256 - b;
+ else
+ sum += b;
+ }
+
+ values.Add(ft, (long)Math.Abs(sum));
+ }
+
+ FilterType minType = FilterType.None;
+ long minValue = (1 << 62) - 1;
+
+ foreach (FilterType ft in values.Keys)
+ {
+ if (values[ft] < minValue)
+ {
+ minType = ft;
+ minValue = values[ft];
+ }
+ }
+
+ return minType;
+ }
+
+ private void FillAdam7Tables(int width, int height)
+ {
+ // yCounts[pass] = count of lines in pass
+ // xCounts[line][pass] = count of pass in each line
+ int[] yCounts = new int[7];
+ int[][] xCounts = new int[8][];
+
+ for (int i = 0; i < 7; i++)
+ xCounts[i] = new int[7];
+ }
+
+ ///
+ /// Reads the image pixels and stores them in Bitmap.
+ ///
+ /// The image bytes.
+ private void Read(byte[] data)
+ {
+ // optimized for speed (it's very slow already)
+
+ IHDRChunk hdr = _image.Chunks["IHDR"] as IHDRChunk;
+ int width = (int)hdr.Width;
+ int height = (int)hdr.Height;
+ byte bitDepth = hdr.BitDepth;
+
+ _bitmap = CreateBitmap(hdr.ColorType, hdr.BitDepth, width, height);
+ _scanlineLength = Utils.IntCeilDiv(Utils.GetBitsPerPixel(hdr.ColorType, hdr.BitDepth) * width, 8) + 1;
+ _bpp = Utils.GetBytesPerPixel(hdr.ColorType, hdr.BitDepth); // bytes per pixel
+
+ byte[] decoded = null;
+
+ // palette
+ PLTEChunk palette = _image.Chunks["PLTE"] as PLTEChunk;
+
+ #region tRNS Chunk
+
+ tRNSChunk trns = _image.Chunks["tRNS"] as tRNSChunk;
+ int tGray1 = -1;
+ int tGray2 = -1;
+ int tGray4 = -1;
+ int tGray8 = -1;
+ int tGray16 = -1;
+ Color tRgb8 = Color.FromArgb(0, 0, 0, 0);
+ Color tRgb16 = Color.FromArgb(0, 0, 0, 0);
+ int[] tPalette = null;
+
+ if (trns != null)
+ {
+ tGray1 = trns.Gray & 0x1;
+ tGray2 = trns.Gray & 0x3;
+ tGray4 = trns.Gray & 0xf;
+ tGray8 = trns.Gray & 0xff;
+ tGray16 = trns.Gray;
+ tRgb8 = Color.FromArgb(trns.Red & 0xff, trns.Green & 0xff, trns.Blue & 0xff);
+ tRgb16 = Color.FromArgb(trns.Red * 255 / 65535, trns.Green * 255 / 65535, trns.Blue * 255 / 65535);
+
+ if (palette != null)
+ {
+ tPalette = new int[palette.Entries.Length];
+
+ for (int i = 0; i < tPalette.Length; i++)
+ {
+ if (i < trns.PaletteAlpha.Length)
+ tPalette[i] = trns.PaletteAlpha[i];
+ else
+ tPalette[i] = 255;
+ }
+ }
+ }
+ else
+ {
+ if (palette != null)
+ {
+ tPalette = new int[palette.Entries.Length];
+
+ for (int i = 0; i < tPalette.Length; i++)
+ tPalette[i] = 255;
+ }
+ }
+
+ #endregion
+
+ if (hdr.InterlaceMethod == InterlaceMethod.Adam7)
+ {
+ #region Adam7
+
+ for (int pass = 0; pass < _adam7.Length; pass++)
+ {
+
+ }
+
+ #endregion
+ }
+ else
+ {
+ #region Normal
+
+ for (int line = 0; line < data.Length / _scanlineLength; line++)
+ {
+ decoded = this.Defilter(data, line, decoded);
+
+ switch (hdr.ColorType)
+ {
+ case ColorType.Grayscale:
+ if (bitDepth == 1)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
+ {
+ int[] pixels = new int[8];
+
+ for (int j = 0; j < 8; j++)
+ pixels[j] = (decoded[i] >> (7 - j)) & 0x1;
+
+ for (int j = 0; j < 8; j++)
+ if (i * 8 + j < hdr.Width)
+ _bitmap.SetPixel(i * 8 + j, line,
+ Utils.MakeGray(pixels[j] == tGray1 ? 0 : 255, pixels[j] * 255));
+ }
+ }
+ else if (bitDepth == 2)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
+ {
+ int[] pixels = new int[4];
+
+ for (int j = 0; j < 4; j++)
+ pixels[j] = (decoded[i] >> ((3 - j) * 2)) & 0x3;
+
+ for (int j = 0; j < 4; j++)
+ if (i * 4 + j < hdr.Width)
+ _bitmap.SetPixel(i * 4 + j, line,
+ Utils.MakeGray(pixels[j] == tGray2 ? 0 : 255, pixels[j] * 255 / 3));
+ }
+ }
+ else if (bitDepth == 4)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
+ {
+ int pixel1 = decoded[i] >> 4; // upper four bits
+ int pixel2 = decoded[i] & 0xf; // lower two bits
+
+ _bitmap.SetPixel(i * 2, line, Utils.MakeGray(
+ pixel1 == tGray4 ? 0 : 255, pixel1 * 255 / 15));
+
+ if (i * 2 + 1 < hdr.Width)
+ _bitmap.SetPixel(i * 2 + 1, line, Utils.MakeGray(
+ pixel2 == tGray4 ? 0 : 255, pixel2 * 255 / 15));
+ }
+ }
+ else if (bitDepth == 8)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line,
+ Utils.MakeGray(decoded[i] == tGray8 ? 0 : 255, decoded[i]));
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ {
+ int value = Utils.BytesToUShort(decoded, i * 2, Utils.Endianness.Big);
+
+ _bitmap.SetPixel(i, line, Utils.MakeGray(
+ value == tGray16 ? 0 : 255, value * 255 / 65535));
+ }
+ }
+
+ break;
+
+ case ColorType.GrayscaleWithAlpha:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ decoded[i * 2 + 1],
+ decoded[i * 2], decoded[i * 2], decoded[i * 2]
+ ));
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ Utils.BytesToUShort(decoded, i * 4 + 2, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 4, Utils.Endianness.Big) * 255 / 65535
+ ));
+ }
+
+ break;
+
+ case ColorType.IndexedColor:
+ if (bitDepth == 1)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
+ {
+ int[] pixels = new int[8];
+
+ for (int j = 0; j < 8; j++)
+ pixels[j] = (decoded[i] >> (7 - j)) & 0x1;
+
+ for (int j = 0; j < 8; j++)
+ if (i * 8 + j < hdr.Width)
+ _bitmap.SetPixel(i * 8 + j, line,
+ Color.FromArgb(tPalette[pixels[j]], palette.Entries[pixels[j]]));
+ }
+ }
+ else if (bitDepth == 2)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
+ {
+ int[] pixels = new int[4];
+
+ for (int j = 0; j < 4; j++)
+ pixels[j] = (decoded[i] >> ((3 - j) * 2)) & 0x3;
+
+ for (int j = 0; j < 4; j++)
+ if (i * 4 + j < hdr.Width)
+ _bitmap.SetPixel(i * 4 + j, line,
+ Color.FromArgb(tPalette[pixels[j]], palette.Entries[pixels[j]]));
+ }
+ }
+ else if (bitDepth == 4)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
+ {
+ int pixel1 = decoded[i] >> 4; // upper four bits
+ int pixel2 = decoded[i] & 0xf; // lower two bits
+
+ _bitmap.SetPixel(i * 2, line,
+ Color.FromArgb(tPalette[pixel1], palette.Entries[pixel1]));
+
+ if (i * 2 + 1 < hdr.Width)
+ _bitmap.SetPixel(i * 2 + 1, line,
+ Color.FromArgb(tPalette[pixel2], palette.Entries[pixel2]));
+ }
+ }
+ else if (bitDepth == 8)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line,
+ Color.FromArgb(tPalette[decoded[i]], palette.Entries[decoded[i]]));
+ }
+
+ break;
+
+ case ColorType.Truecolor:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ {
+ Color c = Color.FromArgb(
+ decoded[i * 3],
+ decoded[i * 3 + 1],
+ decoded[i * 3 + 2]
+ );
+
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ Utils.ColorsEqual(c, tRgb8) ? 0 : 255, c));
+ }
+ }
+ else if (bitDepth == 16)
+ {
+ // .NET doesn't support 16-bit bit depths
+ for (int i = 0; i < hdr.Width; i++)
+ {
+ Color c = Color.FromArgb(
+ Utils.BytesToUShort(decoded, i * 6, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 6 + 2, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 6 + 4, Utils.Endianness.Big) * 255 / 65535
+ );
+
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ Utils.ColorsEqual(c, tRgb16) ? 0 : 255, c));
+ }
+ }
+
+ break;
+
+ case ColorType.TruecolorWithAlpha:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ decoded[i * 4 + 3],
+ decoded[i * 4],
+ decoded[i * 4 + 1],
+ decoded[i * 4 + 2]));
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < hdr.Width; i++)
+ _bitmap.SetPixel(i, line, Color.FromArgb(
+ Utils.BytesToUShort(decoded, i * 8 + 6, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 8, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 8 + 2, Utils.Endianness.Big) * 255 / 65535,
+ Utils.BytesToUShort(decoded, i * 8 + 4, Utils.Endianness.Big) * 255 / 65535
+ ));
+ }
+
+ break;
+
+ default:
+ throw new Exception("Invalid color type.");
+ }
+ }
+
+ #endregion
+ }
+ }
+
+ ///
+ /// Saves the PNG bitmap to the specified file.
+ ///
+ /// The file name.
+ public void Save(string path)
+ {
+ this.Save(path, FileMode.OpenOrCreate);
+ }
+
+ ///
+ /// Saves the PNG bitmap to the specified file.
+ ///
+ /// The file name.
+ /// The file mode.
+ public void Save(string path, FileMode mode)
+ {
+ FileStream fs = new FileStream(path, mode);
+
+ this.Save(fs);
+
+ fs.Close();
+ }
+
+ ///
+ /// Saves the PNG image to a stream.
+ ///
+ /// The stream to write to.
+ public void Save(Stream s)
+ {
+ IHDRChunk hdr = _image.Chunks["IHDR"] as IHDRChunk;
+ int width = (int)hdr.Width;
+ int height = (int)hdr.Height;
+
+ _scanlineLength = Utils.IntCeilDiv(Utils.GetBitsPerPixel(hdr.ColorType, hdr.BitDepth) * width, 8) + 1;
+ _bpp = Utils.GetBytesPerPixel(hdr.ColorType, hdr.BitDepth); // bytes per pixel
+
+ byte bitDepth = hdr.BitDepth;
+
+ MemoryStream data = new MemoryStream();
+ BinaryWriter bwdata = new BinaryWriter(data);
+ byte[] scanline = null;
+
+ for (int line = 0; line < height; line++)
+ {
+ byte[] prevLine = scanline;
+
+ scanline = new byte[_scanlineLength - 1];
+
+ switch (hdr.ColorType)
+ {
+ case ColorType.Grayscale:
+ if (bitDepth == 1)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 8); i++)
+ {
+ byte b = 0;
+
+ for (int j = 0; j < 8; j++)
+ {
+ if (i * 8 + j < hdr.Width)
+ {
+ Color c = _bitmap.GetPixel(i * 8 + j, line);
+ byte value = (byte)((c.R + c.G + c.B) / 3);
+
+ b |= (byte)(((value / 255) >> (7 - j)) & 0x1);
+ }
+ }
+
+ scanline[i] = b;
+ }
+ }
+ else if (bitDepth == 2)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 4); i++)
+ {
+ byte b = 0;
+
+ for (int j = 0; j < 4; j++)
+ {
+ if (i * 4 + j < hdr.Width)
+ {
+ Color c = _bitmap.GetPixel(i * 4 + j, line);
+ byte value = (byte)((c.R + c.G + c.B) / 3);
+
+ b |= (byte)(((value * 3 / 255) >> ((3 - j) * 2)) & 0x3);
+ }
+ }
+
+ scanline[i] = b;
+ }
+ }
+ else if (bitDepth == 4)
+ {
+ for (int i = 0; i < Utils.IntCeilDiv(width, 2); i++)
+ {
+ byte b = 0;
+
+ Color c = _bitmap.GetPixel(i * 2, line);
+ byte value = (byte)((c.R + c.G + c.B) / 3);
+
+ b |= (byte)((value * 15 / 255) << 4);
+
+ if (i * 2 + 1 < width)
+ {
+ c = _bitmap.GetPixel(i * 2 + 1, line);
+ value = (byte)((c.R + c.G + c.B) / 3);
+
+ b |= (byte)(value * 15 / 255);
+ }
+
+ scanline[i] = b;
+ }
+ }
+ else if (bitDepth == 8)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i] = (byte)((c.R + c.G + c.B) / 3);
+ }
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+ byte value = (byte)((c.R + c.G + c.B) / 3);
+
+ scanline[i * 2] = (byte)((value * 65535 / 255) >> 8);
+ scanline[i * 2 + 1] = (byte)((value * 65535 / 255) & 0xff);
+ }
+ }
+
+ break;
+
+ case ColorType.GrayscaleWithAlpha:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i * 2] = (byte)((c.R + c.G + c.B) / 3);
+ scanline[i * 2 + 1] = c.A;
+ }
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+ byte value = (byte)((c.R + c.G + c.B) / 3);
+
+ scanline[i * 4] = (byte)((value * 65535 / 255) >> 8);
+ scanline[i * 4 + 1] = (byte)((value * 65535 / 255) & 0xff);
+ scanline[i * 4 + 2] = (byte)((c.A * 65535 / 255) >> 8);
+ scanline[i * 4 + 3] = (byte)((c.A * 65535 / 255) & 0xff);
+ }
+ }
+
+ break;
+
+ case ColorType.IndexedColor:
+
+
+ break;
+
+ case ColorType.Truecolor:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i * 3] = c.R;
+ scanline[i * 3 + 1] = c.G;
+ scanline[i * 3 + 2] = c.B;
+ }
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i * 6] = (byte)((c.R * 65535 / 255) >> 8);
+ scanline[i * 6 + 1] = (byte)((c.R * 65535 / 255) & 0xff);
+ scanline[i * 6 + 2] = (byte)((c.G * 65535 / 255) >> 8);
+ scanline[i * 6 + 3] = (byte)((c.G * 65535 / 255) & 0xff);
+ scanline[i * 6 + 4] = (byte)((c.B * 65535 / 255) >> 8);
+ scanline[i * 6 + 5] = (byte)((c.B * 65535 / 255) & 0xff);
+ }
+ }
+
+ break;
+
+ case ColorType.TruecolorWithAlpha:
+ if (bitDepth == 8)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i * 4] = c.R;
+ scanline[i * 4 + 1] = c.G;
+ scanline[i * 4 + 2] = c.B;
+ scanline[i * 4 + 3] = c.A;
+ }
+ }
+ else if (bitDepth == 16)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ Color c = _bitmap.GetPixel(i, line);
+
+ scanline[i * 8] = (byte)((c.R * 65535 / 255) >> 8);
+ scanline[i * 8 + 1] = (byte)((c.R * 65535 / 255) & 0xff);
+ scanline[i * 8 + 2] = (byte)((c.G * 65535 / 255) >> 8);
+ scanline[i * 8 + 3] = (byte)((c.G * 65535 / 255) & 0xff);
+ scanline[i * 8 + 4] = (byte)((c.B * 65535 / 255) >> 8);
+ scanline[i * 8 + 5] = (byte)((c.B * 65535 / 255) & 0xff);
+ scanline[i * 8 + 6] = (byte)((c.A * 65535 / 255) >> 8);
+ scanline[i * 8 + 7] = (byte)((c.A * 65535 / 255) & 0xff);
+ }
+ }
+
+ break;
+
+ default:
+ throw new Exception("Invalid color type.");
+ }
+
+ byte[] filtered = this.Filter(scanline, line, this.GetBestFilterMethod(scanline, line, prevLine), prevLine);
+
+ bwdata.Write(filtered);
+ }
+
+ this.SaveChunks(data.ToArray());
+ _image.Write(s, true);
+
+ bwdata.Close();
+ }
+
+ ///
+ /// Modifies the underlying image's chunks to save the image.
+ ///
+ /// The uncompressed image data.
+ private void SaveChunks(byte[] data)
+ {
+ // remove all existing data chunks
+ _image.Chunks.RemoveAll(new Predicate(
+ delegate(Chunk c)
+ {
+ if (c.Type == "IDAT") return true; else return false;
+ }));
+
+ // insert our new data chunk just before the end
+ for (int i = 0; i < _image.Chunks.Count; i++)
+ {
+ if (_image.Chunks[i].Type == "IEND")
+ {
+ IDATChunk idat = new IDATChunk(_image);
+ MemoryStream ms = new MemoryStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(ms, new Deflater(CompressionLevel.Level));
+ BinaryWriter bwdata = new BinaryWriter(dos);
+
+ bwdata.Write(data);
+ dos.Finish();
+ idat.Data = ms.ToArray();
+ bwdata.Close();
+ ms.Close();
+
+ _image.Chunks.Insert(i, idat);
+
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Gets the underlying PNG image.
+ ///
+ public PNGImage Image
+ {
+ get { return _image; }
+ }
+
+ ///
+ /// Gets the bitmap.
+ ///
+ public Bitmap Bitmap
+ {
+ get { return _bitmap; }
+ }
+ }
+}
diff --git a/PNGNet/PNGImage.cs b/PNGNet/PNGImage.cs
new file mode 100644
index 0000000..e142181
--- /dev/null
+++ b/PNGNet/PNGImage.cs
@@ -0,0 +1,377 @@
+/*
+ * PNG.Net
+ *
+ * Copyright (C) 2008 wj32
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+namespace PNGNet
+{
+ ///
+ /// This exception indicates that the PNG image has an invalid header.
+ ///
+ public class InvalidHeaderException : Exception
+ {
+ public InvalidHeaderException(string message) : base(message) { }
+ }
+
+ ///
+ /// Indicates that the PNG image is invalid.
+ ///
+ public class InvalidImageException : Exception
+ {
+ public InvalidImageException(string message) : base(message) { }
+ }
+
+ ///
+ /// Indicates that the PNG image has invalid compressed data.
+ ///
+ public class InvalidCompressedDataException : Exception
+ {
+ public InvalidCompressedDataException(string message) : base(message) { }
+ }
+
+ ///
+ /// Represents a PNG image and provides methods to read and write chunks.
+ ///
+ public sealed class PNGImage
+ {
+ [Flags]
+ public enum PNGImageOptions
+ {
+ ///
+ /// Specifies that errors in ancillary chunks should be fatal.
+ ///
+ AncillaryChunkErrors
+ }
+
+ public const uint MaxLength = 2147483647;
+ public static PNGImageOptions Options;
+
+ private byte[] _header = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+ private ChunkCollection _chunks = new ChunkCollection();
+ private Dictionary _classes = new Dictionary();
+ private bool _ccMod = false;
+
+ ///
+ /// Creates a PNG image with no chunks.
+ ///
+ public PNGImage() { }
+
+ ///
+ /// Creates a PNG image from a stream.
+ ///
+ /// The stream to read from.
+ public PNGImage(Stream s) : this(s, new Type[0]) { }
+
+ ///
+ /// Creates a PNG image from a stream.
+ ///
+ /// The stream to read from.
+ /// Any custom chunk classes you may wish to specify.
+ public PNGImage(Stream s, Type[] customHandlers)
+ {
+ this.LoadClasses(customHandlers);
+
+ byte[] header = new byte[8];
+
+ s.Read(header, 0, 8);
+
+ if (!Utils.BytesEqual(header, _header))
+ throw new InvalidHeaderException("Invalid PNG image header.");
+
+ uint chunkNumber = 0;
+
+ while (s.Position < s.Length)
+ {
+ uint goodcrc, crc32;
+ int length;
+ string type;
+ byte[] data;
+
+ length = Utils.ReadInt(s, Utils.Endianness.Big);
+
+ // this decoder does NOT support chunk lengths greater than 2^31-5 because of limitations with uint.
+ if (length > MaxLength - 4)
+ throw new InvalidChunkLengthException(
+ string.Format("Length {0} for chunk {1} exceeds 2^31-5.", length, chunkNumber));
+
+ type = Utils.ReadString(s, 4);
+
+ // terminate on critical unrecognized chunk
+ if (char.IsUpper(type[0]) && !_classes.ContainsKey(type))
+ throw new InvalidChunkException(string.Format("Critical chunk {0} was not recognized.", type));
+
+ data = new byte[length + 4];
+ ASCIIEncoding.ASCII.GetBytes(type).CopyTo(data, 0);
+
+ s.Read(data, 4, length);
+
+ goodcrc = Utils.ReadUInt(s, Utils.Endianness.Big);
+ crc32 = CRC32.Hash(0, data, 0, length + 4);
+
+ /*if (goodcrc != crc32)
+ throw new CorruptChunkDataException(
+ string.Format("Invalid CRC32 (actual value is 0x{0:x8}, specified value is 0x{1:x8}) for {2} chunk.", crc32, goodcrc, type));
+ */
+ Chunk newChunk = null;
+
+ try
+ {
+ newChunk = Activator.CreateInstance(GetChunkHandler(type), data, this) as Chunk;
+ }
+ catch (Exception ex)
+ {
+ if (char.IsUpper(type[0]))
+ throw ex;
+ else if ((Options & PNGImageOptions.AncillaryChunkErrors) != 0)
+ throw ex;
+ }
+
+ _chunks.Add(newChunk);
+
+ if (type == "IEND")
+ break;
+
+ chunkNumber++;
+ }
+
+ this.Verify();
+ }
+
+ ///
+ /// Checks the image's chunks for conformance to the PNG specification.
+ ///
+ public void Verify()
+ {
+ // verify the critical chunks
+ // IHDR and IEND
+ if (_chunks["IHDR"] == null)
+ throw new InvalidImageException("Critical chunk IHDR was not found.");
+ if (_chunks[0].Type != "IHDR")
+ throw new InvalidImageException("IHDR chunk must appear first.");
+
+ if (_chunks["IEND"] == null)
+ throw new InvalidImageException("Critical chunk IEND was not found.");
+ if (_chunks[_chunks.Count - 1].Type != "IEND")
+ throw new InvalidImageException("IEND chunk must appear last.");
+
+ // PLTE
+ IHDRChunk hdr = _chunks["IHDR"] as IHDRChunk;
+
+ if (hdr.ColorType == ColorType.IndexedColor)
+ if (_chunks["PLTE"] == null)
+ throw new InvalidImageException(
+ "Critical chunk PLTE required for color type 3 (indexed color) was not found.");
+
+ if (hdr.ColorType == ColorType.Grayscale || hdr.ColorType == ColorType.GrayscaleWithAlpha)
+ if (_chunks["PLTE"] != null)
+ throw new InvalidImageException(
+ "PLTE chunk cannot appear for color types 0 and 4 (grayscale and grayscale with alpha).");
+
+ // IDAT
+ if (_chunks["IDAT"] == null)
+ throw new InvalidImageException("Critical chunk IDAT was not found.");
+
+ // verify chunk counts
+ Dictionary counts = new Dictionary();
+
+ foreach (Chunk c in _chunks)
+ if (!counts.ContainsKey(c.GetType()))
+ counts.Add(c.GetType(), 1);
+ else
+ counts[c.GetType()]++;
+
+ foreach (Type t in counts.Keys)
+ {
+ if (_classes.ContainsValue(t))
+ {
+ ChunkAttribute a = t.GetCustomAttributes(typeof(ChunkAttribute), true)[0] as ChunkAttribute;
+
+ if (a.AllowMultiple == false && counts[t] > 1)
+ throw new InvalidImageException(
+ string.Format("Multiple instances ({0}) of {1} chunk are not allowed.", counts[t], a.Type));
+ }
+ }
+
+ // verify ancillary chunks
+ if (_chunks["iCCP"] != null && _chunks["sRGB"] != null)
+ throw new InvalidImageException("iCCP chunk and sRGB chunk cannot be both present.");
+
+ // verify chunk ordering
+ int plteLoc, idatLoc;
+
+ plteLoc = _chunks.IndexOf("PLTE");
+ idatLoc = _chunks.IndexOf("IDAT");
+
+ // verify that IDAT chunks are consecutive
+ bool idatEnded = false;
+
+ for (int i = idatLoc; i < _chunks.Count; i++)
+ {
+ if (_chunks[i].Type == "IDAT" && idatEnded)
+ throw new InvalidImageException("IDAT chunks must be consecutive.");
+ else if (_chunks[i].Type != "IDAT" && !idatEnded)
+ idatEnded = true;
+ }
+
+ // PLTE must be before IDAT
+ if (plteLoc >= idatLoc)
+ throw new InvalidImageException("PLTE chunk must be before first IDAT chunk.");
+
+ // verify other chunks' ordering
+ string[] beforePLTEbeforeIDAT = new string[] { "cHRM", "gAMA", "iCCP", "sBIT", "sRGB" };
+ string[] afterPLTEbeforeIDAT = new string[] { "bKGD", "hIST", "tRNS" };
+ string[] beforeIDAT = new string[] { "pHYs", "sPLT" };
+
+ for (int i = 0; i < _chunks.Count; i++)
+ {
+ if (Utils.ArrayContains(beforePLTEbeforeIDAT, _chunks[i].Type))
+ {
+ if (((plteLoc == -1) ? false : (i >= plteLoc)) || i >= idatLoc)
+ throw new InvalidImageException(
+ string.Format("{0} chunk must be before PLTE chunk and first IDAT chunk.", _chunks[i].Type));
+ }
+
+ if (Utils.ArrayContains(afterPLTEbeforeIDAT, _chunks[i].Type))
+ {
+ if (((plteLoc == -1) ? false : (i <= plteLoc)) || i >= idatLoc)
+ throw new InvalidImageException(
+ string.Format("{0} must be after PLTE chunk and before first IDAT chunk.", _chunks[i].Type));
+ }
+
+ if (Utils.ArrayContains(beforeIDAT, _chunks[i].Type))
+ {
+ if (i >= idatLoc)
+ throw new InvalidImageException(
+ string.Format("{0} must be before first IDAT chunk.", _chunks[i].Type));
+ }
+ }
+ }
+
+ ///
+ /// Writes the PNG image to a file.
+ ///
+ /// The filename.
+ public void Write(string path)
+ {
+ Write(path, FileMode.OpenOrCreate);
+ }
+
+ ///
+ /// Writes the PNG image to a file.
+ ///
+ /// The filename.
+ /// The file mode.
+ public void Write(string path, FileMode mode)
+ {
+ FileStream fs = new FileStream(path, mode);
+
+ Write(fs, true);
+ fs.Close();
+ }
+
+ ///
+ /// Writes the PNG image to a stream.
+ ///
+ /// The stream to which the image is written.
+ public void Write(Stream s, bool verify)
+ {
+ if (verify)
+ this.Verify();
+
+ BinaryWriter bw = new BinaryWriter(s);
+
+ bw.Write(_header);
+
+ foreach (Chunk c in _chunks)
+ c.Write(s);
+
+ bw.Flush();
+ bw.Close();
+ }
+
+ ///
+ /// Gets a copy of the standard PNG file header.
+ ///
+ public byte[] Header
+ {
+ get { return (byte[])_header.Clone(); }
+ }
+
+ ///
+ /// Gets the chunk handler classes.
+ ///
+ internal Dictionary Classes
+ {
+ get { return _classes; }
+ }
+
+ ///
+ /// Gets the chunk handler class for the specified type.
+ ///
+ /// The PNG chunk type.
+ /// The handler class.
+ public Type GetChunkHandler(string type)
+ {
+ Type classType = typeof(Chunk);
+
+ if (_classes.ContainsKey(type))
+ classType = _classes[type];
+
+ return classType;
+ }
+
+ ///
+ /// Loads the list of chunk handler classes from both this assembly and any specified handlers.
+ ///
+ /// The list of custom chunk handlers.
+ private void LoadClasses(Type[] customHandlers)
+ {
+ List types = new List();
+
+ foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
+ types.Add(t);
+
+ foreach (Type t in customHandlers)
+ types.Add(t);
+
+ foreach (Type t in types)
+ {
+ object[] attributes = t.GetCustomAttributes(typeof(ChunkAttribute), false);
+
+ foreach (ChunkAttribute a in attributes)
+ {
+ _classes.Add(a.Type, t);
+ }
+ }
+ }
+
+ ///
+ /// Gets the list chunks in this PNG image.
+ ///
+ public ChunkCollection Chunks
+ {
+ get { return _chunks; }
+ }
+ }
+}
diff --git a/PNGNet/PNGNet.csproj b/PNGNet/PNGNet.csproj
new file mode 100644
index 0000000..f01fdaa
--- /dev/null
+++ b/PNGNet/PNGNet.csproj
@@ -0,0 +1,99 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.21022
+ 2.0
+ {D5CB46E9-E69B-4B45-A15A-C0087833DD4D}
+ Library
+ Properties
+ PNGNet
+ PNGNet
+ v2.0
+ 512
+
+
+
+
+ 3.5
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PNGNet/Properties/AssemblyInfo.cs b/PNGNet/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f5adcb8
--- /dev/null
+++ b/PNGNet/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PNGNet")]
+[assembly: AssemblyDescription("PNG Library for .NET")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("wj32")]
+[assembly: AssemblyProduct("PNGNet")]
+[assembly: AssemblyCopyright("Copyright © wj32 2008. Licensed under the GNU LGPL v3.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5680bf06-46e1-414b-b915-f01bf0005429")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/PNGNet/Utils.cs b/PNGNet/Utils.cs
new file mode 100644
index 0000000..4502b28
--- /dev/null
+++ b/PNGNet/Utils.cs
@@ -0,0 +1,285 @@
+/*
+ * PNG.Net
+ *
+ * Copyright (C) 2008 wj32
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Drawing;
+
+namespace PNGNet
+{
+ public static class Utils
+ {
+ public enum Endianness
+ {
+ Little, Big
+ }
+
+ public static bool ArrayContains(T[] array, T element)
+ {
+ foreach (T e in array)
+ if (e.Equals(element))
+ return true;
+
+ return false;
+ }
+
+ public static bool BytesEqual(byte[] b1, byte[] b2)
+ {
+ for (int i = 0; i < b1.Length; i++)
+ if (b1[i] != b2[i])
+ return false;
+
+ return true;
+ }
+
+ public static int BytesToInt(byte[] data, Endianness type)
+ {
+ if (type == Endianness.Little)
+ {
+ return (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ }
+ else if (type == Endianness.Big)
+ {
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+
+ public static uint BytesToUInt(byte[] data, Endianness type)
+ {
+ return BytesToUInt(data, 0, type);
+ }
+
+ public static uint BytesToUInt(byte[] data, int offset, Endianness type)
+ {
+ if (type == Endianness.Little)
+ {
+ return (uint)(data[offset]) | (uint)(data[offset + 1] << 8) |
+ (uint)(data[offset + 2] << 16) | (uint)(data[offset + 3] << 24);
+ }
+ else if (type == Endianness.Big)
+ {
+ return (uint)(data[offset] << 24) | (uint)(data[offset + 1] << 16) |
+ (uint)(data[offset + 2] << 8) | (uint)(data[offset + 3]);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+
+ public static ushort BytesToUShort(byte[] data, Endianness type)
+ {
+ return BytesToUShort(data, 0, type);
+ }
+
+ public static ushort BytesToUShort(byte[] data, int offset, Endianness type)
+ {
+ if (type == Endianness.Little)
+ {
+ return (ushort)(data[offset] | (data[offset + 1] << 8));
+ }
+ else if (type == Endianness.Big)
+ {
+ return (ushort)((data[offset] << 8) | data[offset + 1]);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+
+ public static bool ColorsEqual(Color a, Color b)
+ {
+ return (a.R == b.R) && (a.G == b.G) && (a.B == b.B) && (a.A == b.A);
+ }
+
+ public static int IntCeilDiv(int a, int b)
+ {
+ return (int)Math.Ceiling(((double)a / b));
+ }
+
+ public static byte[] IntToBytes(int n, Endianness type)
+ {
+ byte[] data = new byte[4];
+
+ if (type == Endianness.Little)
+ {
+ data[0] = (byte)(n & 0xff);
+ data[1] = (byte)((n >> 8) & 0xff);
+ data[2] = (byte)((n >> 16) & 0xff);
+ data[3] = (byte)((n >> 24) & 0xff);
+ }
+ else if (type == Endianness.Big)
+ {
+ data[0] = (byte)((n >> 24) & 0xff);
+ data[1] = (byte)((n >> 16) & 0xff);
+ data[2] = (byte)((n >> 8) & 0xff);
+ data[3] = (byte)(n & 0xff);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+
+ return data;
+ }
+
+ public static int GetBitsPerPixel(ColorType ct, int bpp)
+ {
+ switch (ct)
+ {
+ case ColorType.Grayscale:
+ return bpp;
+ case ColorType.GrayscaleWithAlpha:
+ return bpp * 2;
+ case ColorType.IndexedColor:
+ return bpp;
+ case ColorType.Truecolor:
+ return bpp * 3;
+ case ColorType.TruecolorWithAlpha:
+ return bpp * 4;
+ default:
+ throw new Exception("Invalid color type.");
+ }
+ }
+
+ public static int GetBytesPerPixel(ColorType ct, int bpp)
+ {
+ return GetBitsPerPixel(ct, bpp) / 8;
+ }
+
+ public static Color MakeGray(int value)
+ {
+ return Color.FromArgb(value, value, value);
+ }
+
+ public static Color MakeGray(int alpha, int value)
+ {
+ return Color.FromArgb(alpha, value, value, value);
+ }
+
+ public static byte[] ReverseBytes(byte[] data)
+ {
+ byte[] newdata = new byte[data.Length];
+
+ for (int i = 0; i < data.Length; i++)
+ newdata[i] = data[data.Length - i - 1];
+
+ return newdata;
+ }
+
+ public static uint ReverseEndian(uint n)
+ {
+ uint b0 = n & 0xff;
+ uint b1 = (n >> 8) & 0xff;
+ uint b2 = (n >> 16) & 0xff;
+ uint b3 = (n >> 24) & 0xff;
+
+ b0 <<= 24;
+ b1 <<= 16;
+ b2 <<= 8;
+
+ return b0 | b1 | b2 | b3;
+ }
+
+ public static int ReadInt(Stream s, Utils.Endianness type)
+ {
+ byte[] buffer = new byte[4];
+
+ if (s.Read(buffer, 0, 4) == 0)
+ throw new EndOfStreamException();
+
+ return BytesToInt(buffer, type);
+ }
+
+ public static string ReadString(Stream s, int length)
+ {
+ byte[] buffer = new byte[length];
+
+ if (s.Read(buffer, 0, length) == 0)
+ throw new EndOfStreamException();
+
+ return System.Text.ASCIIEncoding.ASCII.GetString(buffer);
+ }
+
+ public static uint ReadUInt(Stream s, Utils.Endianness type)
+ {
+ byte[] buffer = new byte[4];
+
+ if (s.Read(buffer, 0, 4) == 0)
+ throw new EndOfStreamException();
+
+ return BytesToUInt(buffer, type);
+ }
+
+ public static byte[] UIntToBytes(uint n, Endianness type)
+ {
+ byte[] data = new byte[4];
+
+ if (type == Endianness.Little)
+ {
+ data[0] = (byte)(n & 0xff);
+ data[1] = (byte)((n >> 8) & 0xff);
+ data[2] = (byte)((n >> 16) & 0xff);
+ data[3] = (byte)((n >> 24) & 0xff);
+ }
+ else if (type == Endianness.Big)
+ {
+ data[0] = (byte)((n >> 24) & 0xff);
+ data[1] = (byte)((n >> 16) & 0xff);
+ data[2] = (byte)((n >> 8) & 0xff);
+ data[3] = (byte)(n & 0xff);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+
+ return data;
+ }
+
+ public static byte[] UShortToBytes(ushort n, Endianness type)
+ {
+ byte[] data = new byte[2];
+
+ if (type == Endianness.Little)
+ {
+ data[0] = (byte)(n & 0xff);
+ data[1] = (byte)((n >> 8) & 0xff);
+ }
+ else if (type == Endianness.Big)
+ {
+ data[0] = (byte)((n >> 8) & 0xff);
+ data[1] = (byte)(n & 0xff);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+
+ return data;
+ }
+ }
+}