diff --git a/src/ImageProcessor/Formats/Png/PngDecoder.cs b/src/ImageProcessor/Formats/Png/PngDecoder.cs
index ca4af1338..7c97b6e24 100644
--- a/src/ImageProcessor/Formats/Png/PngDecoder.cs
+++ b/src/ImageProcessor/Formats/Png/PngDecoder.cs
@@ -31,7 +31,7 @@ namespace ImageProcessor.Formats
/// - Palette Index with alpha (8 bit).
/// - Palette Index without alpha (8 bit).
///
- ///
+ ///
///
public class PngDecoder : IImageDecoder
{
diff --git a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
index aa0f37990..d0bdc8698 100644
--- a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
@@ -16,8 +16,8 @@ namespace ImageProcessor.Formats
using System.Linq;
using System.Text;
- using ICSharpCode.SharpZipLib.Checksums;
- using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+ //using ICSharpCode.SharpZipLib.Checksums;
+ //using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
///
/// Performs the png decoding operation.
diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs
index 26f3fe89d..c747d934e 100644
--- a/src/ImageProcessor/Formats/Png/PngEncoder.cs
+++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs
@@ -8,8 +8,8 @@ namespace ImageProcessor.Formats
using System;
using System.IO;
- using ICSharpCode.SharpZipLib.Checksums;
- using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+ //using ICSharpCode.SharpZipLib.Checksums;
+ //using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
///
/// Image encoder for writing image data to a stream in png format.
diff --git a/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs
new file mode 100644
index 000000000..0b9a54dea
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs
@@ -0,0 +1,192 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// Computes Adler32 checksum for a stream of data. An Adler32
+ /// checksum is not as reliable as a CRC32 checksum, but a lot faster to
+ /// compute.
+ ///
+ /// The specification for Adler32 may be found in RFC 1950.
+ /// ZLIB Compressed Data Format Specification version 3.3)
+ ///
+ ///
+ /// From that document:
+ ///
+ /// "ADLER32 (Adler-32 checksum)
+ /// This contains a checksum value of the uncompressed data
+ /// (excluding any dictionary data) computed according to Adler-32
+ /// algorithm. This algorithm is a 32-bit extension and improvement
+ /// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
+ /// standard.
+ ///
+ /// Adler-32 is composed of two sums accumulated per byte: s1 is
+ /// the sum of all bytes, s2 is the sum of all s1 values. Both sums
+ /// are done modulo 65521. s1 is initialized to 1, s2 to zero. The
+ /// Adler-32 checksum is stored as s2*65536 + s1 in most-
+ /// significant-byte first (network) order."
+ ///
+ /// "8.2. The Adler-32 algorithm
+ ///
+ /// The Adler-32 algorithm is much faster than the CRC32 algorithm yet
+ /// still provides an extremely low probability of undetected errors.
+ ///
+ /// The modulo on unsigned long accumulators can be delayed for 5552
+ /// bytes, so the modulo operation time is negligible. If the bytes
+ /// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
+ /// and order sensitive, unlike the first sum, which is just a
+ /// checksum. That 65521 is prime is important to avoid a possible
+ /// large class of two-byte errors that leave the check unchanged.
+ /// (The Fletcher checksum uses 255, which is not prime and which also
+ /// makes the Fletcher check insensitive to single byte changes 0 -
+ /// 255.)
+ ///
+ /// The sum s1 is initialized to 1 instead of zero to make the length
+ /// of the sequence part of s2, so that the length does not have to be
+ /// checked separately. (Any sequence of zeroes has a Fletcher
+ /// checksum of zero.)"
+ ///
+ ///
+ ///
+ public sealed class Adler32 : IChecksum
+ {
+ ///
+ /// largest prime smaller than 65536
+ ///
+ const uint BASE = 65521;
+
+ ///
+ /// Returns the Adler32 data checksum computed so far.
+ ///
+ public long Value
+ {
+ get
+ {
+ return this.checksum;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Creates a new instance of the Adler32 class.
+ /// The checksum starts off with a value of 1.
+ ///
+ public Adler32()
+ {
+ this.Reset();
+ }
+
+ ///
+ /// Resets the Adler32 checksum to the initial value.
+ ///
+ public void Reset()
+ {
+ this.checksum = 1;
+ }
+
+ ///
+ /// Updates the checksum with a byte value.
+ ///
+ ///
+ /// The data value to add. The high byte of the int is ignored.
+ ///
+ public void Update(int value)
+ {
+ // We could make a length 1 byte array and call update again, but I
+ // would rather not have that overhead
+ uint s1 = this.checksum & 0xFFFF;
+ uint s2 = this.checksum >> 16;
+
+ s1 = (s1 + ((uint)value & 0xFF)) % BASE;
+ s2 = (s1 + s2) % BASE;
+
+ this.checksum = (s2 << 16) + s1;
+ }
+
+ ///
+ /// Updates the checksum with an array of bytes.
+ ///
+ ///
+ /// The source of the data to update with.
+ ///
+ public void Update(byte[] buffer)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+
+ this.Update(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Updates the checksum with the bytes taken from the array.
+ ///
+ ///
+ /// an array of bytes
+ ///
+ ///
+ /// the start of the data used for this update
+ ///
+ ///
+ /// the number of bytes to use for this update
+ ///
+ public void Update(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException("offset", "cannot be negative");
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count", "cannot be negative");
+ }
+
+ if (offset >= buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException("offset", "not a valid index into buffer");
+ }
+
+ if (offset + count > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException("count", "exceeds buffer size");
+ }
+
+ //(By Per Bothner)
+ uint s1 = this.checksum & 0xFFFF;
+ uint s2 = this.checksum >> 16;
+
+ while (count > 0)
+ {
+ // We can defer the modulo operation:
+ // s1 maximally grows from 65521 to 65521 + 255 * 3800
+ // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
+ int n = 3800;
+ if (n > count)
+ {
+ n = count;
+ }
+ count -= n;
+ while (--n >= 0)
+ {
+ s1 = s1 + (uint)(buffer[offset++] & 0xff);
+ s2 = s2 + s1;
+ }
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ this.checksum = (s2 << 16) | s1;
+ }
+
+ #region Instance Fields
+ uint checksum;
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs b/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs
new file mode 100644
index 000000000..f476143fe
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs
@@ -0,0 +1,194 @@
+//
+// Copyright © James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
+ /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+ ///
+ /// Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ /// with the lowest powers in the most significant bit. Then adding polynomials
+ /// is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ /// one. If we call the above polynomial p, and represent a byte as the
+ /// polynomial q, also with the lowest power in the most significant bit (so the
+ /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ /// where a mod b means the remainder after dividing a by b.
+ ///
+ /// This calculation is done using the shift-register method of multiplying and
+ /// taking the remainder. The register is initialized to zero, and for each
+ /// incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ /// x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ /// out is a one). We start with the highest power (least significant bit) of
+ /// q and repeat for all eight bits of q.
+ ///
+ /// The table is simply the CRC of all possible eight bit values. This is all
+ /// the information needed to generate CRC's on data a byte at a time for all
+ /// combinations of CRC register values and incoming bytes.
+ ///
+ public sealed class Crc32 : IChecksum
+ {
+ const uint CrcSeed = 0xFFFFFFFF;
+
+ readonly static uint[] CrcTable = new uint[] {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
+ 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
+ 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
+ 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
+ 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
+ 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
+ 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
+ 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
+ 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
+ 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
+ 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
+ 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
+ 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
+ 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
+ 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
+ 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
+ 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
+ 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
+ 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
+ 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
+ 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
+ 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
+ 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
+ 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
+ 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
+ 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
+ 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
+ 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
+ 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
+ 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
+ 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
+ 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
+ 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
+ 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
+ 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
+ 0x2D02EF8D
+ };
+
+ internal static uint ComputeCrc32(uint oldCrc, byte value)
+ {
+ return (uint)(Crc32.CrcTable[(oldCrc ^ value) & 0xFF] ^ (oldCrc >> 8));
+ }
+
+ ///
+ /// The crc data checksum so far.
+ ///
+ uint crc;
+
+ ///
+ /// Returns the CRC32 data checksum computed so far.
+ ///
+ public long Value
+ {
+ get
+ {
+ return (long)this.crc;
+ }
+ set
+ {
+ this.crc = (uint)value;
+ }
+ }
+
+ ///
+ /// Resets the CRC32 data checksum as if no update was ever called.
+ ///
+ public void Reset()
+ {
+ this.crc = 0;
+ }
+
+ ///
+ /// Updates the checksum with the int bval.
+ ///
+ ///
+ /// the byte is taken as the lower 8 bits of value
+ ///
+ public void Update(int value)
+ {
+ this.crc ^= CrcSeed;
+ this.crc = CrcTable[(this.crc ^ value) & 0xFF] ^ (this.crc >> 8);
+ this.crc ^= CrcSeed;
+ }
+
+ ///
+ /// Updates the checksum with the bytes taken from the array.
+ ///
+ ///
+ /// buffer an array of bytes
+ ///
+ public void Update(byte[] buffer)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+
+ this.Update(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Adds the byte array to the data checksum.
+ ///
+ ///
+ /// The buffer which contains the data
+ ///
+ ///
+ /// The offset in the buffer where the data starts
+ ///
+ ///
+ /// The number of data bytes to update the CRC with.
+ ///
+ public void Update(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException("count", "Count cannot be less than zero");
+ }
+
+ if (offset < 0 || offset + count > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException("offset");
+ }
+
+ this.crc ^= CrcSeed;
+
+ while (--count >= 0)
+ {
+ this.crc = CrcTable[(this.crc ^ buffer[offset++]) & 0xFF] ^ (this.crc >> 8);
+ }
+
+ this.crc ^= CrcSeed;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs
new file mode 100644
index 000000000..31a913609
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs
@@ -0,0 +1,26 @@
+namespace ImageProcessor.Formats
+{
+ ///
+ /// Strategies for deflater
+ ///
+ 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
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs
new file mode 100644
index 000000000..cbabedd90
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs
@@ -0,0 +1,555 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ //using ICSharpCode.SharpZipLib.Zip.Compression;
+
+ ///
+ /// This is the Deflater class. The deflater class compresses input
+ /// with the deflate algorithm described in RFC 1951. It has several
+ /// compression levels and three different strategies described below.
+ ///
+ /// 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 Deflater
+ {
+ #region Deflater Documentation
+ /*
+ * The Deflater can do the following state transitions:
+ *
+ * (1) -> INIT_STATE ----> 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)
+ *
+ */
+ #endregion
+ #region Public Constants
+ ///
+ /// The best and slowest compression level. This tries to find very
+ /// long and distant string repetitions.
+ ///
+ public const int BEST_COMPRESSION = 9;
+
+ ///
+ /// The worst but fastest compression level.
+ ///
+ public const int BEST_SPEED = 1;
+
+ ///
+ /// The default compression level.
+ ///
+ public const int DEFAULT_COMPRESSION = -1;
+
+ ///
+ /// This level won't compress at all but output uncompressed blocks.
+ ///
+ public const int NO_COMPRESSION = 0;
+
+ ///
+ /// The compression method. This is the only method supported so far.
+ /// There is no need to use this constant at all.
+ ///
+ public const int DEFLATED = 8;
+ #endregion
+ #region Local Constants
+ private const int IS_SETDICT = 0x01;
+ private const int IS_FLUSHING = 0x04;
+ private const int IS_FINISHING = 0x08;
+
+ private const int INIT_STATE = 0x00;
+ private const int SETDICT_STATE = 0x01;
+ // private static int INIT_FINISHING_STATE = 0x08;
+ // private static int SETDICT_FINISHING_STATE = 0x09;
+ private const int BUSY_STATE = 0x10;
+ private const int FLUSHING_STATE = 0x14;
+ private const int FINISHING_STATE = 0x1c;
+ private const int FINISHED_STATE = 0x1e;
+ private const int CLOSED_STATE = 0x7f;
+ #endregion
+ #region Constructors
+ ///
+ /// 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 level) : this(level, 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 Zlib/RFC1950 header at the
+ /// beginning and the adler checksum at the end of the output. This is
+ /// useful for the GZIP/PKZIP formats.
+ ///
+ /// if lvl is out of range.
+ public Deflater(int level, bool noZlibHeaderOrFooter)
+ {
+ if (level == DEFAULT_COMPRESSION)
+ {
+ level = 6;
+ }
+ else if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
+ {
+ throw new ArgumentOutOfRangeException("level");
+ }
+
+ pending = new DeflaterPending();
+ engine = new DeflaterEngine(pending);
+ this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
+ SetStrategy(DeflateStrategy.Default);
+ SetLevel(level);
+ Reset();
+ }
+ #endregion
+
+ ///
+ /// 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 = (noZlibHeaderOrFooter ? 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 long TotalIn
+ {
+ get
+ {
+ return engine.TotalIn;
+ }
+ }
+
+ ///
+ /// Gets the number of output bytes so far.
+ ///
+ public long 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 number of data bytes of input.
+ ///
+ ///
+ /// if the buffer was Finish()ed or if previous input is still pending.
+ ///
+ public void SetInput(byte[] input, int offset, int count)
+ {
+ if ((state & IS_FINISHING) != 0)
+ {
+ throw new InvalidOperationException("Finish() already called");
+ }
+ engine.SetInput(input, offset, count);
+ }
+
+ ///
+ /// 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 level)
+ {
+ if (level == DEFAULT_COMPRESSION)
+ {
+ level = 6;
+ }
+ else if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
+ {
+ throw new ArgumentOutOfRangeException("level");
+ }
+
+ if (this.level != level)
+ {
+ this.level = level;
+ engine.SetLevel(level);
+ }
+ }
+
+ ///
+ /// Get current compression level
+ ///
+ /// Returns the current compression level
+ public int GetLevel()
+ {
+ return level;
+ }
+
+ ///
+ /// 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 strategy)
+ {
+ engine.Strategy = strategy;
+ }
+
+ ///
+ /// Deflates the current input block with to the given array.
+ ///
+ ///
+ /// The buffer where compressed data is stored
+ ///
+ ///
+ /// The number of compressed bytes added to the output, or 0 if either
+ /// IsNeedingInput() or IsFinished returns true or length is zero.
+ ///
+ public int Deflate(byte[] output)
+ {
+ return Deflate(output, 0, output.Length);
+ }
+
+ ///
+ /// Deflates the current input block to the given array.
+ ///
+ ///
+ /// Buffer to store the compressed data.
+ ///
+ ///
+ /// Offset into the output array.
+ ///
+ ///
+ /// The maximum number of bytes that may be stored.
+ ///
+ ///
+ /// The number of compressed bytes added to the output, or 0 if either
+ /// needsInput() or finished() returns true or length is zero.
+ ///
+ ///
+ /// If Finish() was previously called.
+ ///
+ ///
+ /// If offset 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
+ * is 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();
+
+ // Compressed data is complete. Write footer information if required.
+ if (!noZlibHeaderOrFooter)
+ {
+ 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[] dictionary)
+ {
+ SetDictionary(dictionary, 0, dictionary.Length);
+ }
+
+ ///
+ /// Sets the dictionary which should be used in the deflate process.
+ /// The dictionary is 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 data
+ ///
+ ///
+ /// The index where dictionary information commences.
+ ///
+ ///
+ /// The number of bytes in the dictionary.
+ ///
+ ///
+ /// If SetInput () or Deflate() were already called or another dictionary was already set.
+ ///
+ public void SetDictionary(byte[] dictionary, int index, int count)
+ {
+ if (state != INIT_STATE)
+ {
+ throw new InvalidOperationException();
+ }
+
+ state = SETDICT_STATE;
+ engine.SetDictionary(dictionary, index, count);
+ }
+
+ #region Instance Fields
+ ///
+ /// Compression level.
+ ///
+ int level;
+
+ ///
+ /// If true no Zlib/RFC1950 headers or footers are generated
+ ///
+ bool noZlibHeaderOrFooter;
+
+ ///
+ /// The current state.
+ ///
+ int state;
+
+ ///
+ /// The total bytes of output written.
+ ///
+ long totalOut;
+
+ ///
+ /// The pending output.
+ ///
+ DeflaterPending pending;
+
+ ///
+ /// The deflater engine.
+ ///
+ DeflaterEngine engine;
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs
new file mode 100644
index 000000000..1b9efbdb1
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs
@@ -0,0 +1,145 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// This class contains constants used for deflation.
+ ///
+ public class DeflaterConstants
+ {
+ ///
+ /// Set to true to enable debugging
+ ///
+ public const bool DEBUGGING = false;
+
+ ///
+ /// Written to Zip file to identify a stored block
+ ///
+ public const int STORED_BLOCK = 0;
+
+ ///
+ /// Identifies static tree in Zip file
+ ///
+ public const int STATIC_TREES = 1;
+
+ ///
+ /// Identifies dynamic tree in Zip file
+ ///
+ public const int DYN_TREES = 2;
+
+ ///
+ /// Header flag indicating a preset dictionary for deflation
+ ///
+ public const int PRESET_DICT = 0x20;
+
+ ///
+ /// Sets internal buffer sizes for Huffman encoding
+ ///
+ public const int DEFAULT_MEM_LEVEL = 8;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int MAX_MATCH = 258;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int MIN_MATCH = 3;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int MAX_WBITS = 15;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int WSIZE = 1 << MAX_WBITS;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int WMASK = WSIZE - 1;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int HASH_SIZE = 1 << HASH_BITS;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int HASH_MASK = HASH_SIZE - 1;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5);
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int DEFLATE_STORED = 0;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int DEFLATE_FAST = 1;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public const int DEFLATE_SLOW = 2;
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 };
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int[] MAX_LAZY = { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 };
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int[] NICE_LENGTH = { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 };
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int[] MAX_CHAIN = { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 };
+
+ ///
+ /// Internal compression engine constant
+ ///
+ public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 };
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs
new file mode 100644
index 000000000..15ae2ec8e
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs
@@ -0,0 +1,757 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// Low level compression engine for deflate algorithm which uses a 32K sliding window
+ /// with secondary compression from Huffman/Shannon-Fano codes.
+ ///
+ ///
+ /// DEFLATE ALGORITHM:
+ ///
+ /// The uncompressed stream is inserted into the window array. When
+ /// the window array is full the first half is thrown away and the
+ /// second half is copied to the beginning.
+ ///
+ /// The head array is a hash table. Three characters build a hash value
+ /// and they the value points to the corresponding index in window of
+ /// the last string with this hash. The prev array implements a
+ /// linked list of matches with the same hash: prev[index & WMASK] points
+ /// to the previous index with the same hash.
+ ///
+ public class DeflaterEngine : DeflaterConstants
+ {
+ ///
+ /// ne more than the maximum upper bounds.
+ ///
+ private const int TooFar = 4096;
+
+ ///
+ /// prev[index & WMASK] points to the previous index that has the
+ /// same hash code as the string starting at index. This way
+ /// entries with the same hash code are in a linked list.
+ /// Note that the array should really be unsigned short, so you need
+ /// to and the values with 0xffff.
+ ///
+ private readonly short[] previousIndex;
+
+ ///
+ /// Hashtable, hashing three characters to an index for window, so
+ /// that window[index]..window[index+2] have this hash code.
+ /// Note that the array should really be unsigned short, so you need
+ /// to and the values with 0xffff.
+ ///
+ private readonly short[] head;
+
+ ///
+ /// Hash index of string to be inserted.
+ ///
+ private int insertHashIndex;
+
+ ///
+ /// Initializes a new instance of the class with a pending buffer.
+ ///
+ /// The pending buffer to use>
+ public DeflaterEngine(DeflaterPending pending)
+ {
+ this.pending = pending;
+ this.huffman = new DeflaterHuffman(pending);
+ this.adler = new Adler32();
+
+ this.window = new byte[2 * WSIZE];
+ this.head = new short[HASH_SIZE];
+ this.previousIndex = new short[WSIZE];
+
+ // We start at index 1, to avoid an implementation deficiency, that
+ // we cannot build a repeat pattern at index 0.
+ this.blockStart = this.strstart = 1;
+ }
+
+ ///
+ /// Get current value of Adler checksum
+ ///
+ public int Adler => unchecked((int)this.adler.Value);
+
+ ///
+ /// Total data processed
+ ///
+ public long TotalIn => this.totalIn;
+
+ ///
+ /// Get or sets the
+ ///
+ public DeflateStrategy Strategy { get; set; }
+
+ ///
+ /// Deflate drives actual compression of data
+ ///
+ /// True to flush input buffers
+ /// Finish deflation with the current input.
+ /// Returns true if progress has been made.
+ public bool Deflate(bool flush, bool finish)
+ {
+ bool progress;
+ do
+ {
+ this.FillWindow();
+ bool canFlush = flush && (this.inputOff == this.inputEnd);
+
+ switch (this.compressionFunction)
+ {
+ case DEFLATE_STORED:
+ progress = this.DeflateStored(canFlush, finish);
+ break;
+ case DEFLATE_FAST:
+ progress = this.DeflateFast(canFlush, finish);
+ break;
+ case DEFLATE_SLOW:
+ progress = this.DeflateSlow(canFlush, finish);
+ break;
+ default:
+ throw new InvalidOperationException("unknown compressionFunction");
+ }
+ }
+ while (this.pending.IsFlushed && progress); // repeat while we have no pending output and progress was made
+ return progress;
+ }
+
+ ///
+ /// Sets input data to be deflated. Should only be called when NeedsInput()
+ /// returns true
+ ///
+ /// The buffer containing input data.
+ /// The offset of the first byte of data.
+ /// The number of bytes of data to use as input.
+ public void SetInput(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+
+ if (this.inputOff < this.inputEnd)
+ {
+ throw new InvalidOperationException("Old input was not completely processed");
+ }
+
+ int end = offset + count;
+
+ // We want to throw an ArrayIndexOutOfBoundsException early. The
+ // check is very tricky: it also handles integer wrap around.
+ if ((offset > end) || (end > buffer.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+
+ this.inputBuf = buffer;
+ this.inputOff = offset;
+ this.inputEnd = end;
+ }
+
+ ///
+ /// Determines if more input is needed.
+ ///
+ /// Return true if input is needed via SetInput
+ public bool NeedsInput()
+ {
+ return this.inputEnd == this.inputOff;
+ }
+
+ ///
+ /// Set compression dictionary
+ ///
+ /// The buffer containing the dictionary data
+ /// The offset in the buffer for the first byte of data
+ /// The length of the dictionary data.
+ public void SetDictionary(byte[] buffer, int offset, int length)
+ {
+ this.adler.Update(buffer, offset, length);
+ if (length < MIN_MATCH)
+ {
+ return;
+ }
+
+ if (length > MAX_DIST)
+ {
+ offset += length - MAX_DIST;
+ length = MAX_DIST;
+ }
+
+ Array.Copy(buffer, offset, this.window, this.strstart, length);
+
+ this.UpdateHash();
+ --length;
+ while (--length > 0)
+ {
+ this.InsertString();
+ this.strstart++;
+ }
+
+ this.strstart += 2;
+ this.blockStart = this.strstart;
+ }
+
+ ///
+ /// Reset internal state
+ ///
+ public void Reset()
+ {
+ this.huffman.Reset();
+ this.adler.Reset();
+ this.blockStart = this.strstart = 1;
+ this.lookahead = 0;
+ this.totalIn = 0;
+ this.prevAvailable = false;
+ this.matchLen = MIN_MATCH - 1;
+
+ for (int i = 0; i < HASH_SIZE; i++)
+ {
+ this.head[i] = 0;
+ }
+
+ for (int i = 0; i < WSIZE; i++)
+ {
+ this.previousIndex[i] = 0;
+ }
+ }
+
+ ///
+ /// Reset Adler checksum
+ ///
+ public void ResetAdler()
+ {
+ this.adler.Reset();
+ }
+
+ ///
+ /// Set the deflate level (0-9)
+ ///
+ /// The value to set the level to.
+ public void SetLevel(int level)
+ {
+ if ((level < 0) || (level > 9))
+ {
+ throw new ArgumentOutOfRangeException(nameof(level));
+ }
+
+ this.goodLength = GOOD_LENGTH[level];
+ this.maxLazy = MAX_LAZY[level];
+ this.niceLength = NICE_LENGTH[level];
+ this.maxChain = MAX_CHAIN[level];
+
+ if (COMPR_FUNC[level] != this.compressionFunction)
+ {
+ switch (this.compressionFunction)
+ {
+ case DEFLATE_STORED:
+ if (this.strstart > this.blockStart)
+ {
+ this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
+ this.blockStart = this.strstart;
+ }
+
+ this.UpdateHash();
+ break;
+
+ case DEFLATE_FAST:
+ if (this.strstart > this.blockStart)
+ {
+ this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart,
+ false);
+ this.blockStart = this.strstart;
+ }
+
+ break;
+
+ case DEFLATE_SLOW:
+ if (this.prevAvailable)
+ {
+ this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ }
+
+ if (this.strstart > this.blockStart)
+ {
+ this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
+ this.blockStart = this.strstart;
+ }
+
+ this.prevAvailable = false;
+ this.matchLen = MIN_MATCH - 1;
+ break;
+ }
+
+ this.compressionFunction = COMPR_FUNC[level];
+ }
+ }
+
+ ///
+ /// Fill the window
+ ///
+ 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 (this.strstart >= WSIZE + MAX_DIST)
+ {
+ this.SlideWindow();
+ }
+
+ // If there is not enough lookahead, but still some input left,
+ // read in the input
+ while (this.lookahead < MIN_LOOKAHEAD && this.inputOff < this.inputEnd)
+ {
+ int more = (2 * WSIZE) - this.lookahead - this.strstart;
+
+ if (more > this.inputEnd - this.inputOff)
+ {
+ more = this.inputEnd - this.inputOff;
+ }
+
+ Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
+ this.adler.Update(this.inputBuf, this.inputOff, more);
+
+ this.inputOff += more;
+ this.totalIn += more;
+ this.lookahead += more;
+ }
+
+ if (this.lookahead >= MIN_MATCH)
+ {
+ this.UpdateHash();
+ }
+ }
+
+ private void UpdateHash()
+ {
+ this.insertHashIndex = (this.window[this.strstart] << HASH_SHIFT) ^ this.window[this.strstart + 1];
+ }
+
+ ///
+ /// Inserts the current string in the head hash and returns the previous
+ /// value for this hash.
+ ///
+ /// The previous hash value
+ private int InsertString()
+ {
+ short match;
+ int hash = ((this.insertHashIndex << HASH_SHIFT) ^ this.window[this.strstart + (MIN_MATCH - 1)]) & HASH_MASK;
+
+ this.previousIndex[this.strstart & WMASK] = match = this.head[hash];
+ this.head[hash] = unchecked((short)this.strstart);
+ this.insertHashIndex = hash;
+ return match & 0xffff;
+ }
+
+ private void SlideWindow()
+ {
+ Array.Copy(this.window, WSIZE, this.window, 0, WSIZE);
+ this.matchStart -= WSIZE;
+ this.strstart -= WSIZE;
+ this.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 = this.head[i] & 0xffff;
+ this.head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
+ }
+
+ // Slide the prev table.
+ for (int i = 0; i < WSIZE; i++)
+ {
+ int m = this.previousIndex[i] & 0xffff;
+ this.previousIndex[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
+ }
+ }
+
+ ///
+ /// Find the best (longest) string in the window matching the
+ /// string starting at strstart.
+ ///
+ /// Preconditions:
+ ///
+ /// strstart + MAX_MATCH <= window.length.
+ ///
+ /// The current match.
+ /// True if a match greater than the minimum length is found
+ private bool FindLongestMatch(int curMatch)
+ {
+ int chainLength = this.maxChain;
+ int length = this.niceLength;
+ short[] previous = this.previousIndex;
+ int scan = this.strstart;
+ int bestEnd = this.strstart + this.matchLen;
+ int bestLength = Math.Max(this.matchLen, MIN_MATCH - 1);
+
+ int limit = Math.Max(this.strstart - MAX_DIST, 0);
+
+ int strend = this.strstart + MAX_MATCH - 1;
+ byte scanEnd1 = this.window[bestEnd - 1];
+ byte scanEnd = this.window[bestEnd];
+
+ // Do not waste too much time if we already have a good match:
+ if (bestLength >= this.goodLength)
+ {
+ chainLength >>= 2;
+ }
+
+ // Do not look for matches beyond the end of the input. This is necessary
+ // to make deflate deterministic.
+ if (length > this.lookahead)
+ {
+ length = this.lookahead;
+ }
+
+ do
+ {
+ if (this.window[curMatch + bestLength] != scanEnd ||
+ this.window[curMatch + bestLength - 1] != scanEnd1 ||
+ this.window[curMatch] != this.window[scan] ||
+ this.window[curMatch + 1] != this.window[scan + 1])
+ {
+ continue;
+ }
+
+ int match = curMatch + 2;
+ scan += 2;
+
+ // We check for insufficient lookahead only every 8th comparison;
+ // the 256th check will be made at strstart + 258.
+ while (
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ this.window[++scan] == this.window[++match] &&
+ (scan < strend))
+ {
+ // Do nothing
+ }
+
+ if (scan > bestEnd)
+ {
+ this.matchStart = curMatch;
+ bestEnd = scan;
+ bestLength = scan - this.strstart;
+
+ if (bestLength >= length)
+ {
+ break;
+ }
+
+ scanEnd1 = this.window[bestEnd - 1];
+ scanEnd = this.window[bestEnd];
+ }
+
+ scan = this.strstart;
+ } while ((curMatch = previous[curMatch & WMASK] & 0xffff) > limit && --chainLength != 0);
+
+ this.matchLen = Math.Min(bestLength, this.lookahead);
+ return this.matchLen >= MIN_MATCH;
+ }
+
+ private bool DeflateStored(bool flush, bool finish)
+ {
+ if (!flush && (this.lookahead == 0))
+ {
+ return false;
+ }
+
+ this.strstart += this.lookahead;
+ this.lookahead = 0;
+
+ int storedLength = this.strstart - this.blockStart;
+
+ if ((storedLength >= MAX_BLOCK_SIZE) || // Block is full
+ (this.blockStart < WSIZE && storedLength >= MAX_DIST) || // Block may move out of window
+ flush)
+ {
+ bool lastBlock = finish;
+ if (storedLength > MAX_BLOCK_SIZE)
+ {
+ storedLength = MAX_BLOCK_SIZE;
+ lastBlock = false;
+ }
+
+ this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock);
+ this.blockStart += storedLength;
+ return !lastBlock;
+ }
+
+ return true;
+ }
+
+ private bool DeflateFast(bool flush, bool finish)
+ {
+ if (this.lookahead < MIN_LOOKAHEAD && !flush)
+ {
+ return false;
+ }
+
+ while (this.lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (this.lookahead == 0)
+ {
+ // We are flushing everything
+ this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
+ this.blockStart = this.strstart;
+ return false;
+ }
+
+ if (this.strstart > (2 * WSIZE) - MIN_LOOKAHEAD)
+ {
+ /* slide window, as FindLongestMatch needs this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ this.SlideWindow();
+ }
+
+ int hashHead;
+ if (this.lookahead >= MIN_MATCH &&
+ (hashHead = this.InsertString()) != 0 &&
+ this.Strategy != DeflateStrategy.HuffmanOnly &&
+ this.strstart - hashHead <= MAX_DIST &&
+ this.FindLongestMatch(hashHead))
+ {
+ // longestMatch sets matchStart and matchLen
+ bool full = this.huffman.TallyDist(this.strstart - this.matchStart, this.matchLen);
+
+ this.lookahead -= this.matchLen;
+ if (this.matchLen <= this.maxLazy && this.lookahead >= MIN_MATCH)
+ {
+ while (--this.matchLen > 0)
+ {
+ ++this.strstart;
+ this.InsertString();
+ }
+
+ ++this.strstart;
+ }
+ else
+ {
+ this.strstart += this.matchLen;
+ if (this.lookahead >= MIN_MATCH - 1)
+ {
+ this.UpdateHash();
+ }
+ }
+
+ this.matchLen = MIN_MATCH - 1;
+ if (!full)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ // No match found
+ this.huffman.TallyLit(this.window[this.strstart] & 0xff);
+ ++this.strstart;
+ --this.lookahead;
+ }
+
+ if (this.huffman.IsFull())
+ {
+ bool lastBlock = finish && (this.lookahead == 0);
+ this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock);
+ this.blockStart = this.strstart;
+ return !lastBlock;
+ }
+ }
+
+ return true;
+ }
+
+ private bool DeflateSlow(bool flush, bool finish)
+ {
+ if (this.lookahead < MIN_LOOKAHEAD && !flush)
+ {
+ return false;
+ }
+
+ while (this.lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (this.lookahead == 0)
+ {
+ if (this.prevAvailable)
+ {
+ this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ }
+
+ this.prevAvailable = false;
+
+ // We are flushing everything
+ this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart,
+ finish);
+ this.blockStart = this.strstart;
+ return false;
+ }
+
+ if (this.strstart >= (2 * WSIZE) - MIN_LOOKAHEAD)
+ {
+ // slide window, as FindLongestMatch needs this.
+ // This should only happen when flushing and the window
+ // is almost full.
+ this.SlideWindow();
+ }
+
+ int prevMatch = this.matchStart;
+ int prevLen = this.matchLen;
+ if (this.lookahead >= MIN_MATCH)
+ {
+
+ int hashHead = this.InsertString();
+
+ if (this.Strategy != DeflateStrategy.HuffmanOnly &&
+ hashHead != 0 &&
+ this.strstart - hashHead <= MAX_DIST &&
+ this.FindLongestMatch(hashHead))
+ {
+
+ // longestMatch sets matchStart and matchLen
+
+ // Discard match if too small and too far away
+ if (this.matchLen <= 5 && (this.Strategy == DeflateStrategy.Filtered || (this.matchLen == MIN_MATCH && this.strstart - this.matchStart > TooFar)))
+ {
+ this.matchLen = MIN_MATCH - 1;
+ }
+ }
+ }
+
+ // previous match was better
+ if ((prevLen >= MIN_MATCH) && (this.matchLen <= prevLen))
+ {
+ this.huffman.TallyDist(this.strstart - 1 - prevMatch, prevLen);
+ prevLen -= 2;
+ do
+ {
+ this.strstart++;
+ this.lookahead--;
+ if (this.lookahead >= MIN_MATCH)
+ {
+ this.InsertString();
+ }
+ } while (--prevLen > 0);
+
+ this.strstart++;
+ this.lookahead--;
+ this.prevAvailable = false;
+ this.matchLen = MIN_MATCH - 1;
+ }
+ else
+ {
+ if (this.prevAvailable)
+ {
+ this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ }
+
+ this.prevAvailable = true;
+ this.strstart++;
+ this.lookahead--;
+ }
+
+ if (this.huffman.IsFull())
+ {
+ int len = this.strstart - this.blockStart;
+ if (this.prevAvailable)
+ {
+ len--;
+ }
+
+ bool lastBlock = finish && (this.lookahead == 0) && !this.prevAvailable;
+ this.huffman.FlushBlock(this.window, this.blockStart, len, lastBlock);
+ this.blockStart += len;
+ return !lastBlock;
+ }
+ }
+
+ return true;
+ }
+
+ private int matchStart;
+
+ // Length of best match
+ private int matchLen;
+
+ // Set if previous match exists
+ private bool prevAvailable;
+
+ private int blockStart;
+
+ ///
+ /// Points to the current character in the window.
+ ///
+ private int strstart;
+
+ ///
+ /// lookahead is the number of characters starting at strstart in
+ /// window that are valid.
+ /// So window[strstart] until window[strstart+lookahead-1] are valid
+ /// characters.
+ ///
+ private int lookahead;
+
+ ///
+ /// This array contains the part of the uncompressed stream that
+ /// is of relevance. The current character is indexed by strstart.
+ ///
+ private byte[] window;
+
+ private int maxChain;
+
+ private int maxLazy;
+
+ private int niceLength;
+
+ private int goodLength;
+
+ ///
+ /// The current compression function.
+ ///
+ private int compressionFunction;
+
+ ///
+ /// The input data for compression.
+ ///
+ private byte[] inputBuf;
+
+ ///
+ /// The total bytes of input read.
+ ///
+ private long totalIn;
+
+ ///
+ /// The offset into inputBuf, where input data starts.
+ ///
+ private int inputOff;
+
+ ///
+ /// The end offset of the input data.
+ ///
+ private int inputEnd;
+
+ private DeflaterPending pending;
+
+ private DeflaterHuffman huffman;
+
+ ///
+ /// The adler checksum
+ ///
+ private Adler32 adler;
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs
new file mode 100644
index 000000000..8ea1369c1
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs
@@ -0,0 +1,958 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// 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
+ {
+ const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
+ const int LITERAL_NUM = 286;
+
+ // Number of distance codes
+ const int DIST_NUM = 30;
+ // Number of codes used to transfer bit lengths
+ const int BITLEN_NUM = 19;
+
+ // repeat previous bit length 3-6 times (2 bits of repeat count)
+ const int REP_3_6 = 16;
+ // repeat a zero length 3-10 times (3 bits of repeat count)
+ const int REP_3_10 = 17;
+ // repeat a zero length 11-138 times (7 bits of repeat count)
+ const int REP_11_138 = 18;
+
+ const int EOF_SYMBOL = 256;
+
+ // The lengths of the bit length codes are sent in order of decreasing
+ // probability, to avoid transmitting the lengths for unused bit length codes.
+ static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ static readonly byte[] bit4Reverse =
+ {
+ 0,
+ 8,
+ 4,
+ 12,
+ 2,
+ 10,
+ 6,
+ 14,
+ 1,
+ 9,
+ 5,
+ 13,
+ 3,
+ 11,
+ 7,
+ 15
+ };
+
+ static short[] staticLCodes;
+ static byte[] staticLLength;
+ static short[] staticDCodes;
+ static byte[] staticDLength;
+
+ class Tree
+ {
+ #region Instance Fields
+ public short[] freqs;
+
+ public byte[] length;
+
+ public int minNumCodes;
+
+ public int numCodes;
+
+ short[] codes;
+ int[] bl_counts;
+ int maxLength;
+ DeflaterHuffman dh;
+ #endregion
+
+ #region Constructors
+ 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];
+ }
+
+ #endregion
+
+ ///
+ /// Resets the internal state of the tree
+ ///
+ 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]);
+ }
+
+ ///
+ /// Check that all frequencies are zero
+ ///
+ ///
+ /// At least one frequency is non-zero
+ ///
+ 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 InvalidOperationException("!Empty");
+ }
+ }
+
+ ///
+ /// Set static codes and length
+ ///
+ /// new codes
+ /// length for new codes
+ public void SetStaticCodes(short[] staticCodes, byte[] staticLengths)
+ {
+ codes = staticCodes;
+ length = staticLengths;
+ }
+
+ ///
+ /// Build dynamic codes and lengths
+ ///
+ 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);
+ // }
+ }
+
+#if DebugDeflation
+ if ( DeflaterConstants.DEBUGGING && (code != 65536) )
+ {
+ throw new ImageFormatException("Inconsistent bl_counts!");
+ }
+#endif
+ 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]+"),
+ // +bits);
+ // }
+
+ codes[i] = BitReverse(nextCode[bits - 1]);
+ nextCode[bits - 1] += 1 << (16 - bits);
+ }
+ }
+ }
+
+ 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 ImageFormatException("Heap invariant violated");
+ }
+
+ BuildLength(childs);
+ }
+
+ ///
+ /// Get encoded length
+ ///
+ /// Encoded length, the sum of frequencies * lengths
+ public int GetEncodedLength()
+ {
+ int len = 0;
+ for (int i = 0; i < freqs.Length; i++)
+ {
+ len += freqs[i] * length[i];
+ }
+ return len;
+ }
+
+ ///
+ /// Scan a literal or distance tree to determine the frequencies of the codes
+ /// in the bit length tree.
+ ///
+ 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]++;
+ }
+ }
+ }
+
+ ///
+ /// Write tree values
+ ///
+ /// Tree to write
+ 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);
+ }
+ }
+ }
+
+ 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
+ // number 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]]);
+ // }
+ // }
+ }
+
+ }
+
+ #region Instance Fields
+ ///
+ /// Pending buffer to use
+ ///
+ public DeflaterPending pending;
+
+ Tree literalTree;
+ Tree distTree;
+ Tree blTree;
+
+ // Buffer for distances
+ short[] d_buf;
+ byte[] l_buf;
+ int last_lit;
+ int extra_bits;
+ #endregion
+
+ 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;
+ }
+
+ // Distance 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;
+ }
+ }
+
+ ///
+ /// Construct instance with pending buffer
+ ///
+ /// Pending buffer to use
+ 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];
+ }
+
+ ///
+ /// Reset internal state
+ ///
+ public void Reset()
+ {
+ last_lit = 0;
+ extra_bits = 0;
+ literalTree.Reset();
+ distTree.Reset();
+ blTree.Reset();
+ }
+
+ ///
+ /// Write all trees to pending buffer
+ ///
+ /// The number/rank of treecodes to send.
+ 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 DebugDeflation
+ if (DeflaterConstants.DEBUGGING) {
+ blTree.CheckEmpty();
+ }
+#endif
+ }
+
+ ///
+ /// Compress current buffer writing data to pending buffer
+ ///
+ 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 = Lcode(litlen);
+ literalTree.WriteSymbol(lc);
+
+ int bits = (lc - 261) / 4;
+ if (bits > 0 && bits <= 5)
+ {
+ pending.WriteBits(litlen & ((1 << bits) - 1), bits);
+ }
+
+ int dc = Dcode(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 DebugDeflation
+ if (DeflaterConstants.DEBUGGING) {
+ Console.Write("EOF: ");
+ }
+#endif
+ literalTree.WriteSymbol(EOF_SYMBOL);
+
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING) {
+ literalTree.CheckEmpty();
+ distTree.CheckEmpty();
+ }
+#endif
+ }
+
+ ///
+ /// Flush block to output with no compression
+ ///
+ /// Data to write
+ /// Index of first byte to write
+ /// Count of bytes to write
+ /// True if this is the last block
+ public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
+ {
+#if DebugDeflation
+ // if (DeflaterConstants.DEBUGGING) {
+ // //Console.WriteLine("Flushing stored block "+ storedLength);
+ // }
+#endif
+ pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3);
+ pending.AlignToByte();
+ pending.WriteShort(storedLength);
+ pending.WriteShort(~storedLength);
+ pending.WriteBlock(stored, storedOffset, storedLength);
+ Reset();
+ }
+
+ ///
+ /// Flush block to output with compression
+ ///
+ /// Data to flush
+ /// Index of first byte to flush
+ /// Count of bytes to flush
+ /// True if this is the last block
+ public void FlushBlock(byte[] stored, int storedOffset, int storedLength, 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 (storedOffset >= 0 && storedLength + 4 < opt_len >> 3)
+ {
+ // Store Block
+
+ // if (DeflaterConstants.DEBUGGING) {
+ // //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len
+ // + " <= " + static_len);
+ // }
+ FlushStoredBlock(stored, storedOffset, storedLength, 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();
+ }
+ }
+
+ ///
+ /// Get value indicating if internal buffer is full
+ ///
+ /// true if buffer is full
+ public bool IsFull()
+ {
+ return last_lit >= BUFSIZE;
+ }
+
+ ///
+ /// Add literal to buffer
+ ///
+ /// Literal value to add to buffer.
+ /// Value indicating internal buffer is full
+ public bool TallyLit(int literal)
+ {
+ // 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)literal;
+ literalTree.freqs[literal]++;
+ return IsFull();
+ }
+
+ ///
+ /// Add distance code and length to literal and distance trees
+ ///
+ /// Distance code
+ /// Length
+ /// Value indicating if internal buffer is full
+ public bool TallyDist(int distance, int length)
+ {
+ // if (DeflaterConstants.DEBUGGING) {
+ // //Console.WriteLine("[" + distance + "," + length + "]");
+ // }
+
+ d_buf[last_lit] = (short)distance;
+ l_buf[last_lit++] = (byte)(length - 3);
+
+ int lc = Lcode(length - 3);
+ literalTree.freqs[lc]++;
+ if (lc >= 265 && lc < 285)
+ {
+ extra_bits += (lc - 261) / 4;
+ }
+
+ int dc = Dcode(distance - 1);
+ distTree.freqs[dc]++;
+ if (dc >= 4)
+ {
+ extra_bits += dc / 2 - 1;
+ }
+ return IsFull();
+ }
+
+
+ ///
+ /// Reverse the bits of a 16 bit value.
+ ///
+ /// Value to reverse bits
+ /// Value with bits reversed
+ public static short BitReverse(int toReverse)
+ {
+ return (short)(bit4Reverse[toReverse & 0xF] << 12 |
+ bit4Reverse[(toReverse >> 4) & 0xF] << 8 |
+ bit4Reverse[(toReverse >> 8) & 0xF] << 4 |
+ bit4Reverse[toReverse >> 12]);
+ }
+
+ static int Lcode(int length)
+ {
+ if (length == 255)
+ {
+ return 285;
+ }
+
+ int code = 257;
+ while (length >= 8)
+ {
+ code += 4;
+ length >>= 1;
+ }
+ return code + length;
+ }
+
+ static int Dcode(int distance)
+ {
+ int code = 0;
+ while (distance >= 4)
+ {
+ code += 2;
+ distance >>= 1;
+ }
+ return code + distance;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs
new file mode 100644
index 000000000..2f4d9eefd
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs
@@ -0,0 +1,577 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+ using System.IO;
+
+ ///
+ /// A special stream deflating or compressing the bytes that are
+ /// written to it. It uses a Deflater to perform actual deflating.
+ /// Authors of the original java version : Tom Tromey, Jochen Hoenicke
+ ///
+ public class DeflaterOutputStream : Stream
+ {
+ #region Constructors
+ ///
+ /// 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 deflater)
+ : this(baseOutputStream, deflater, 512)
+ {
+ }
+
+ ///
+ /// Creates a new DeflaterOutputStream with the given Deflater and
+ /// buffer size.
+ ///
+ ///
+ /// The output stream where deflated output is written.
+ ///
+ ///
+ /// The underlying deflater to use
+ ///
+ ///
+ /// The buffer size in bytes to use when deflating (minimum value 512)
+ ///
+ ///
+ /// bufsize is less than or equal to zero.
+ ///
+ ///
+ /// baseOutputStream does not support writing
+ ///
+ ///
+ /// deflater instance is null
+ ///
+ public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
+ {
+ if (baseOutputStream == null)
+ {
+ throw new ArgumentNullException("baseOutputStream");
+ }
+
+ if (baseOutputStream.CanWrite == false)
+ {
+ throw new ArgumentException("Must support writing", "baseOutputStream");
+ }
+
+ if (deflater == null)
+ {
+ throw new ArgumentNullException("deflater");
+ }
+
+ if (bufferSize < 512)
+ {
+ throw new ArgumentOutOfRangeException("bufferSize");
+ }
+
+ baseOutputStream_ = baseOutputStream;
+ buffer_ = new byte[bufferSize];
+ deflater_ = deflater;
+ }
+ #endregion
+
+ #region Public API
+ ///
+ /// Finishes the stream by calling finish() on the deflater.
+ ///
+ ///
+ /// Not all input is deflated
+ ///
+ public virtual void Finish()
+ {
+ deflater_.Finish();
+ while (!deflater_.IsFinished)
+ {
+ int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
+ if (len <= 0)
+ {
+ break;
+ }
+
+ if (keys != null)
+ {
+ EncryptBlock(buffer_, 0, len);
+ }
+
+ baseOutputStream_.Write(buffer_, 0, len);
+ }
+
+ if (!deflater_.IsFinished)
+ {
+ throw new ImageFormatException("Can't deflate all input?");
+ }
+
+ baseOutputStream_.Flush();
+
+ if (keys != null)
+ {
+ keys = null;
+ }
+ }
+
+ ///
+ /// Get/set flag indicating ownership of the underlying stream.
+ /// When the flag is true will close the underlying stream also.
+ ///
+ public bool IsStreamOwner
+ {
+ get { return isStreamOwner_; }
+ set { isStreamOwner_ = value; }
+ }
+
+ ///
+ /// Allows client to determine if an entry can be patched after its added
+ ///
+ public bool CanPatchEntries
+ {
+ get
+ {
+ return baseOutputStream_.CanSeek;
+ }
+ }
+
+ #endregion
+
+ #region Encryption
+
+ string password;
+
+ uint[] keys;
+
+ ///
+ /// Get/set the password used for encryption.
+ ///
+ /// When set to null or if the password is empty no encryption is performed
+ public string Password
+ {
+ get
+ {
+ return password;
+ }
+ set
+ {
+ if ((value != null) && (value.Length == 0))
+ {
+ password = null;
+ }
+ else
+ {
+ password = value;
+ }
+ }
+ }
+
+ ///
+ /// Encrypt a block of data
+ ///
+ ///
+ /// Data to encrypt. NOTE the original contents of the buffer are lost
+ ///
+ ///
+ /// Offset of first byte in buffer to encrypt
+ ///
+ ///
+ /// Number of bytes in buffer to encrypt
+ ///
+ protected void EncryptBlock(byte[] buffer, int offset, int length)
+ {
+ for (int i = offset; i < offset + length; ++i)
+ {
+ byte oldbyte = buffer[i];
+ buffer[i] ^= EncryptByte();
+ UpdateKeys(oldbyte);
+ }
+ }
+
+ ///
+ /// Initializes encryption keys based on given .
+ ///
+ /// The password.
+ protected void InitializePassword(string password)
+ {
+ keys = new uint[] {
+ 0x12345678,
+ 0x23456789,
+ 0x34567890
+ };
+
+ byte[] rawPassword = ZipConstants.ConvertToArray(password);
+
+ for (int i = 0; i < rawPassword.Length; ++i)
+ {
+ UpdateKeys((byte)rawPassword[i]);
+ }
+ }
+
+#if !NET_1_1 && !NETCF_2_0 && !NOCRYPTO
+ ///
+ /// Initializes encryption keys based on given password.
+ ///
+ protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
+ out byte[] salt, out byte[] pwdVerifier) {
+ salt = new byte[entry.AESSaltLen];
+ // Salt needs to be cryptographically random, and unique per file
+ if (_aesRnd == null)
+ _aesRnd = new RNGCryptoServiceProvider();
+ _aesRnd.GetBytes(salt);
+ int blockSize = entry.AESKeySize / 8; // bits to bytes
+
+ cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
+ pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
+ }
+#endif
+
+#if NETCF_1_0 || NOCRYPTO
+
+ ///
+ /// Encrypt a single byte
+ ///
+ ///
+ /// The encrypted value
+ ///
+ protected byte EncryptByte()
+ {
+ uint temp = ((keys[2] & 0xFFFF) | 2);
+ return (byte)((temp * (temp ^ 1)) >> 8);
+ }
+
+ ///
+ /// Update encryption keys
+ ///
+ protected void UpdateKeys(byte ch)
+ {
+ keys[0] = Crc32.ComputeCrc32(keys[0], ch);
+ keys[1] = keys[1] + (byte)keys[0];
+ keys[1] = keys[1] * 134775813 + 1;
+ keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
+ }
+#endif
+
+ #endregion
+
+ #region Deflation Support
+ ///
+ /// Deflates everything in the input buffers. This will call
+ /// def.deflate() until all bytes from the input buffers
+ /// are processed.
+ ///
+ protected void Deflate()
+ {
+ while (!deflater_.IsNeedingInput)
+ {
+ int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
+
+ if (deflateCount <= 0)
+ {
+ break;
+ }
+#if NETCF_1_0 || NOCRYPTO
+ if (keys != null)
+#else
+ if (cryptoTransform_ != null)
+#endif
+ {
+ EncryptBlock(buffer_, 0, deflateCount);
+ }
+
+ baseOutputStream_.Write(buffer_, 0, deflateCount);
+ }
+
+ if (!deflater_.IsNeedingInput)
+ {
+ throw new ImageFormatException("DeflaterOutputStream can't deflate all input?");
+ }
+ }
+ #endregion
+
+ #region Stream Overrides
+ ///
+ /// Gets value indicating stream can be read from
+ ///
+ public override bool CanRead
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value indicating if seeking is supported for this stream
+ /// This property always returns false
+ ///
+ public override bool CanSeek
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Get value indicating if this stream supports writing
+ ///
+ public override bool CanWrite
+ {
+ get
+ {
+ return baseOutputStream_.CanWrite;
+ }
+ }
+
+ ///
+ /// Get current length of stream
+ ///
+ public override long Length
+ {
+ get
+ {
+ return baseOutputStream_.Length;
+ }
+ }
+
+ ///
+ /// Gets the current position within the stream.
+ ///
+ /// Any attempt to set position
+ public override long Position
+ {
+ get
+ {
+ return baseOutputStream_.Position;
+ }
+ set
+ {
+ throw new NotSupportedException("Position property not supported");
+ }
+ }
+
+ ///
+ /// Sets the current position of this stream to the given value. Not supported by this class!
+ ///
+ /// The offset relative to the to seek.
+ /// The to seek from.
+ /// The new position in the stream.
+ /// Any access
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException("DeflaterOutputStream Seek not supported");
+ }
+
+ ///
+ /// Sets the length of this stream to the given value. Not supported by this class!
+ ///
+ /// The new stream length.
+ /// Any access
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
+ }
+
+ ///
+ /// Read a byte from stream advancing position by one
+ ///
+ /// The byte read cast to an int. THe value is -1 if at the end of the stream.
+ /// Any access
+ public override int ReadByte()
+ {
+ throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
+ }
+
+ ///
+ /// Read a block of bytes from stream
+ ///
+ /// The buffer to store read data in.
+ /// The offset to start storing at.
+ /// The maximum number of bytes to read.
+ /// The actual number of bytes read. Zero if end of stream is detected.
+ /// Any access
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("DeflaterOutputStream Read not supported");
+ }
+#if !PCL
+ ///
+ /// Asynchronous reads are not supported a NotSupportedException is always thrown
+ ///
+ /// The buffer to read into.
+ /// The offset to start storing data at.
+ /// The number of bytes to read
+ /// The async callback to use.
+ /// The state to use.
+ /// Returns an
+ /// Any access
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");
+ }
+
+ ///
+ /// Asynchronous writes arent supported, a NotSupportedException is always thrown
+ ///
+ /// The buffer to write.
+ /// The offset to begin writing at.
+ /// The number of bytes to write.
+ /// The to use.
+ /// The state object.
+ /// Returns an IAsyncResult.
+ /// Any access
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ throw new NotSupportedException("BeginWrite is not supported");
+ }
+#endif
+ ///
+ /// 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()
+ {
+ deflater_.Flush();
+ Deflate();
+ baseOutputStream_.Flush();
+ }
+
+ ///
+ /// Calls and closes the underlying
+ /// stream when is true.
+ ///
+#if !PCL
+ public override void Close()
+ {
+ if (!isClosed_)
+ {
+ isClosed_ = true;
+
+ try {
+ Finish();
+#if NETCF_1_0 || NOCRYPTO
+ keys =null;
+#else
+ if ( cryptoTransform_ != null ) {
+ GetAuthCodeIfAES();
+ cryptoTransform_.Dispose();
+ cryptoTransform_ = null;
+ }
+#endif
+ }
+ finally {
+ if( isStreamOwner_ ) {
+ baseOutputStream_.Close();
+ }
+ }
+ }
+ }
+#else
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (disposing && !isClosed_)
+ {
+ isClosed_ = true;
+
+ try
+ {
+ Finish();
+ keys = null;
+ }
+ finally
+ {
+ if (isStreamOwner_)
+ {
+ baseOutputStream_.Dispose();
+ }
+ }
+ }
+ }
+#endif
+
+ private void GetAuthCodeIfAES()
+ {
+#if !NET_1_1 && !NETCF_2_0 && !NOCRYPTO
+ if (cryptoTransform_ is ZipAESTransform) {
+ AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
+ }
+#endif
+ }
+
+ ///
+ /// Writes a single byte to the compressed output stream.
+ ///
+ ///
+ /// The byte value.
+ ///
+ public override void WriteByte(byte value)
+ {
+ byte[] b = new byte[1];
+ b[0] = value;
+ Write(b, 0, 1);
+ }
+
+ ///
+ /// Writes 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[] buffer, int offset, int count)
+ {
+ deflater_.SetInput(buffer, offset, count);
+ Deflate();
+ }
+ #endregion
+
+ #region Instance Fields
+ ///
+ /// This buffer is used temporarily to retrieve the bytes from the
+ /// deflater and write them to the underlying output stream.
+ ///
+ byte[] buffer_;
+
+ ///
+ /// The deflater which is used to deflate the stream.
+ ///
+ protected Deflater deflater_;
+
+ ///
+ /// Base stream the deflater depends on.
+ ///
+ protected Stream baseOutputStream_;
+
+ bool isClosed_;
+
+ bool isStreamOwner_ = true;
+ #endregion
+
+ #region Static Fields
+
+#if !NET_1_1 && !NETCF_2_0 && !NOCRYPTO
+ // Static to help ensure that multiple files within a zip will get different random salt
+ private static RNGCryptoServiceProvider _aesRnd;
+#endif
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs
new file mode 100644
index 000000000..70df58382
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs
@@ -0,0 +1,19 @@
+namespace ImageProcessor.Formats
+{
+ ///
+ /// This class stores the pending output of the Deflater.
+ ///
+ /// author of the original java version : Jochen Hoenicke
+ ///
+ public class DeflaterPending : PendingBuffer
+ {
+ ///
+ /// Initializes a new instance of the class.
+ /// Construct instance with default buffer size
+ ///
+ public DeflaterPending()
+ : base(DeflaterConstants.PENDING_BUF_SIZE)
+ {
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs b/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs
new file mode 100644
index 000000000..1325b8662
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs
@@ -0,0 +1,92 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// Defines the contents of the general bit flags field for an archive entry.
+ ///
+ [Flags]
+ public enum GeneralBitFlags
+ {
+ ///
+ /// Bit 0 if set indicates that the file is encrypted
+ ///
+ Encrypted = 0x0001,
+
+ ///
+ /// Bits 1 and 2 - Two bits defining the compression method (only for Method 6 Imploding and 8,9 Deflating)
+ ///
+ Method = 0x0006,
+
+ ///
+ /// Bit 3 if set indicates a trailing data desciptor is appended to the entry data
+ ///
+ Descriptor = 0x0008,
+
+ ///
+ /// Bit 4 is reserved for use with method 8 for enhanced deflation
+ ///
+ ReservedPKware4 = 0x0010,
+
+ ///
+ /// Bit 5 if set indicates the file contains Pkzip compressed patched data.
+ /// Requires version 2.7 or greater.
+ ///
+ Patched = 0x0020,
+
+ ///
+ /// Bit 6 if set indicates strong encryption has been used for this entry.
+ ///
+ StrongEncryption = 0x0040,
+
+ ///
+ /// Bit 7 is currently unused
+ ///
+ Unused7 = 0x0080,
+
+ ///
+ /// Bit 8 is currently unused
+ ///
+ Unused8 = 0x0100,
+
+ ///
+ /// Bit 9 is currently unused
+ ///
+ Unused9 = 0x0200,
+
+ ///
+ /// Bit 10 is currently unused
+ ///
+ Unused10 = 0x0400,
+
+ ///
+ /// Bit 11 if set indicates the filename and
+ /// comment fields for this file must be encoded using UTF-8.
+ ///
+ UnicodeText = 0x0800,
+
+ ///
+ /// Bit 12 is documented as being reserved by PKware for enhanced compression.
+ ///
+ EnhancedCompress = 0x1000,
+
+ ///
+ /// Bit 13 if set indicates that values in the local header are masked to hide
+ /// their actual values, and the central directory is encrypted.
+ ///
+ ///
+ /// Used when encrypting the central directory contents.
+ ///
+ HeaderMasked = 0x2000,
+
+ ///
+ /// Bit 14 is documented as being reserved for use by PKware
+ ///
+ ReservedPkware14 = 0x4000,
+
+ ///
+ /// Bit 15 is documented as being reserved for use by PKware
+ ///
+ ReservedPkware15 = 0x8000
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs b/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs
new file mode 100644
index 000000000..5da32dd32
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs
@@ -0,0 +1,60 @@
+//
+// Copyright © James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor.Formats
+{
+ ///
+ /// Interface to compute a data checksum used by checked input/output streams.
+ /// A data checksum can be updated by one byte or with a byte array. After each
+ /// update the value of the current checksum can be returned by calling
+ /// getValue. The complete checksum object can also be reset
+ /// so it can be used again with new data.
+ ///
+ public interface IChecksum
+ {
+ ///
+ /// Returns the data checksum computed so far.
+ ///
+ long Value
+ {
+ get;
+ }
+
+ ///
+ /// Resets the data checksum as if no update was ever called.
+ ///
+ void Reset();
+
+ ///
+ /// Adds one byte to the data checksum.
+ ///
+ ///
+ /// the data value to add. The high byte of the int is ignored.
+ ///
+ void Update(int value);
+
+ ///
+ /// Updates the data checksum with the bytes taken from the array.
+ ///
+ ///
+ /// buffer an array of bytes
+ ///
+ void Update(byte[] buffer);
+
+ ///
+ /// Adds the byte array to the data checksum.
+ ///
+ ///
+ /// The buffer which contains the data
+ ///
+ ///
+ /// The offset in the buffer where the data starts
+ ///
+ ///
+ /// the number of data bytes to add.
+ ///
+ void Update(byte[] buffer, int offset, int count);
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs
new file mode 100644
index 000000000..c23a15311
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs
@@ -0,0 +1,872 @@
+namespace ImageProcessor.Formats {
+ using System;
+
+ ///
+ /// Inflater is used to decompress data that has been compressed according
+ /// to the "deflate" standard described in rfc1951.
+ ///
+ /// By default Zlib (rfc1950) headers and footers are expected in the input.
+ /// You can use constructor public Inflater(bool noHeader) passing true
+ /// if there is no Zlib header information
+ ///
+ /// 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:
+ ///
+ /// - IsNeedingInput() returns true because the input buffer is empty.
+ /// You have to provide more input with
SetInput().
+ /// NOTE: IsNeedingInput() also returns true when, the stream is finished.
+ ///
+ /// - IsNeedingDictionary() returns true, you have to provide a preset
+ /// dictionary with
SetDictionary().
+ /// - IsFinished 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
+ {
+ #region Constants/Readonly
+ ///
+ /// Copy lengths for literal codes 257..285
+ ///
+ static readonly 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
+ ///
+ static readonly 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
+ ///
+ static readonly 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
+ ///
+ static readonly 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
+ };
+
+ ///
+ /// These are the possible states for an inflater
+ ///
+ const int DECODE_HEADER = 0;
+ const int DECODE_DICT = 1;
+ const int DECODE_BLOCKS = 2;
+ const int DECODE_STORED_LEN1 = 3;
+ const int DECODE_STORED_LEN2 = 4;
+ const int DECODE_STORED = 5;
+ const int DECODE_DYN_HEADER = 6;
+ const int DECODE_HUFFMAN = 7;
+ const int DECODE_HUFFMAN_LENBITS = 8;
+ const int DECODE_HUFFMAN_DIST = 9;
+ const int DECODE_HUFFMAN_DISTBITS = 10;
+ const int DECODE_CHKSUM = 11;
+ const int FINISHED = 12;
+ #endregion
+
+ #region Instance Fields
+ ///
+ /// This variable contains the current state.
+ ///
+ 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.
+ ///
+ 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.
+ ///
+ int neededBits;
+ int repLength;
+ int repDist;
+ 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.
+ ///
+ bool isLastBlock;
+
+ ///
+ /// The total number of inflated bytes.
+ ///
+ long totalOut;
+
+ ///
+ /// The total number of bytes set with setInput(). This is not the
+ /// value returned by the TotalIn property, since this also includes the
+ /// unprocessed input.
+ ///
+ long totalIn;
+
+ ///
+ /// This variable stores the noHeader flag that was given to the constructor.
+ /// True means, that the inflated stream doesn't contain a Zlib header or
+ /// footer.
+ ///
+ bool noHeader;
+
+ StreamManipulator input;
+ OutputWindow outputWindow;
+ InflaterDynHeader dynHeader;
+ InflaterHuffmanTree litlenTree, distTree;
+ Adler32 adler;
+ #endregion
+
+ #region Constructors
+ ///
+ /// Creates a new inflater or RFC1951 decompressor
+ /// RFC1950/Zlib headers and footers will be expected in the input data
+ ///
+ public Inflater() : this(false)
+ {
+ }
+
+ ///
+ /// Creates a new inflater.
+ ///
+ ///
+ /// True if no RFC1950/Zlib header and footer fields are expected in the input data
+ ///
+ /// This is used for GZIPed/Zipped input.
+ ///
+ /// For compatibility with
+ /// Sun JDK you should provide one byte of input more than needed in
+ /// this case.
+ ///
+ public Inflater(bool noHeader)
+ {
+ this.noHeader = noHeader;
+ this.adler = new Adler32();
+ input = new StreamManipulator();
+ outputWindow = new OutputWindow();
+ mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER;
+ }
+ #endregion
+
+ ///
+ /// Resets the inflater so that a new stream can be decompressed. All
+ /// pending input and output will be discarded.
+ ///
+ public void Reset()
+ {
+ mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER;
+ totalIn = 0;
+ totalOut = 0;
+ input.Reset();
+ outputWindow.Reset();
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ isLastBlock = false;
+ adler.Reset();
+ }
+
+ ///
+ /// Decodes a zlib/RFC1950 header.
+ ///
+ ///
+ /// False if more input is needed.
+ ///
+ ///
+ /// The 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 ImageFormatException("Header checksum illegal");
+ }
+
+ if ((header & 0x0f00) != (Deflater.DEFLATED << 8))
+ {
+ throw new ImageFormatException("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 ImageFormatException("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 ImageFormatException("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 ImageFormatException("Inflater unknown mode");
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Decodes the adler checksum after the deflate stream.
+ ///
+ ///
+ /// false if more input is needed.
+ ///
+ ///
+ /// 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 ImageFormatException("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.
+ ///
+ ///
+ /// 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 (noHeader)
+ {
+ 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 ImageFormatException("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 ImageFormatException("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 ImageFormatException("Inflater.Decode unknown mode");
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ 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 index into buffer where the dictionary starts.
+ ///
+ ///
+ /// The number of bytes in the dictionary.
+ ///
+ ///
+ /// No dictionary is needed.
+ ///
+ ///
+ /// The adler checksum for the buffer is invalid
+ ///
+ public void SetDictionary(byte[] buffer, int index, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+
+ if (!IsNeedingDictionary)
+ {
+ throw new InvalidOperationException("Dictionary is not needed");
+ }
+
+ adler.Update(buffer, index, count);
+
+ if ((int)adler.Value != readAdler)
+ {
+ throw new ImageFormatException("Wrong adler checksum");
+ }
+ adler.Reset();
+ outputWindow.CopyDict(buffer, index, count);
+ mode = DECODE_BLOCKS;
+ }
+
+ ///
+ /// Sets the input. This should only be called, if needsInput()
+ /// returns true.
+ ///
+ ///
+ /// the input.
+ ///
+ public void SetInput(byte[] buffer)
+ {
+ SetInput(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Sets the input. This should only be called, if needsInput()
+ /// returns true.
+ ///
+ ///
+ /// The source of input data
+ ///
+ ///
+ /// The index into buffer where the input starts.
+ ///
+ ///
+ /// The number of bytes of input to use.
+ ///
+ ///
+ /// No input is needed.
+ ///
+ ///
+ /// The index and/or count are wrong.
+ ///
+ public void SetInput(byte[] buffer, int index, int count)
+ {
+ input.SetInput(buffer, index, count);
+ totalIn += (long)count;
+ }
+
+ ///
+ /// Inflates the compressed stream to the output buffer. If this
+ /// returns 0, you should check, whether IsNeedingDictionary(),
+ /// IsNeedingInput() or IsFinished() 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 buffer has length 0.
+ ///
+ ///
+ /// if deflated stream is invalid.
+ ///
+ public int Inflate(byte[] buffer)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ return Inflate(buffer, 0, buffer.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 in buffer where storing starts.
+ ///
+ ///
+ /// the maximum number of bytes to output.
+ ///
+ ///
+ /// the number of bytes written to the buffer, 0 if no further output can be produced.
+ ///
+ ///
+ /// if count is less than 0.
+ ///
+ ///
+ /// if the index and / or count are wrong.
+ ///
+ ///
+ /// if deflated stream is invalid.
+ ///
+ public int Inflate(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (count < 0)
+ {
+#if NETCF_1_0
+ throw new ArgumentOutOfRangeException("count");
+#else
+ throw new ArgumentOutOfRangeException(nameof(count), "count cannot be negative");
+#endif
+ }
+
+ if (offset < 0)
+ {
+#if NETCF_1_0
+ throw new ArgumentOutOfRangeException("offset");
+#else
+ throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be negative");
+#endif
+ }
+
+ if (offset + count > buffer.Length)
+ {
+ throw new ArgumentException("count exceeds buffer bounds");
+ }
+
+ // Special case: count may be zero
+ if (count == 0)
+ {
+ if (!IsFinished)
+ { // -jr- 08-Nov-2003 INFLATE_BUG fix..
+ Decode();
+ }
+ return 0;
+ }
+
+ int bytesCopied = 0;
+
+ 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:
+ * IsNeedingInput() and not IsFinished()
+ * implies more output can be produced.
+ */
+ int more = outputWindow.CopyOutput(buffer, offset, count);
+ if (more > 0)
+ {
+ adler.Update(buffer, offset, more);
+ offset += more;
+ bytesCopied += more;
+ totalOut += (long)more;
+ count -= more;
+ if (count == 0)
+ {
+ return bytesCopied;
+ }
+ }
+ }
+ } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM)));
+ return bytesCopied;
+ }
+
+ ///
+ /// 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 long TotalOut
+ {
+ get
+ {
+ return totalOut;
+ }
+ }
+
+ ///
+ /// Gets the total number of processed compressed input bytes.
+ ///
+ ///
+ /// The total number of bytes of processed input bytes.
+ ///
+ public long TotalIn
+ {
+ get
+ {
+ return totalIn - (long)RemainingInput;
+ }
+ }
+
+ ///
+ /// Gets the number of unprocessed input bytes. 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 have not been processed.
+ ///
+ public int RemainingInput
+ {
+ // TODO: This should be a long?
+ get
+ {
+ return input.AvailableBytes;
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs
new file mode 100644
index 000000000..d1811dd3b
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs
@@ -0,0 +1,196 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ class InflaterDynHeader
+ {
+ #region Constants
+ 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 };
+
+ static readonly int[] BL_ORDER =
+ { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ #endregion
+
+ #region Constructors
+ public InflaterDynHeader()
+ {
+ }
+ #endregion
+
+ 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 ImageFormatException();
+ }
+ }
+ 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 ImageFormatException();
+ }
+ 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);
+ }
+
+ #region Instance Fields
+ byte[] blLens;
+ byte[] litdistLens;
+
+ InflaterHuffmanTree blTree;
+
+ ///
+ /// The current decode mode
+ ///
+ int mode;
+ int lnum, dnum, blnum, num;
+ int repSymbol;
+ byte lastLen;
+ int ptr;
+ #endregion
+
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs
new file mode 100644
index 000000000..f6b7332f5
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs
@@ -0,0 +1,225 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// Huffman tree used for inflation
+ ///
+ public class InflaterHuffmanTree
+ {
+ #region Constants
+ const int MAX_BITLEN = 15;
+ #endregion
+
+ #region Instance Fields
+ short[] tree;
+ #endregion
+
+ ///
+ /// Literal length tree
+ ///
+ public static InflaterHuffmanTree defLitLenTree;
+
+ ///
+ /// Distance tree
+ ///
+ public static InflaterHuffmanTree 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 ImageFormatException("InflaterHuffmanTree: static tree length illegal");
+ }
+ }
+
+ #region Constructors
+ ///
+ /// Constructs a Huffman tree from the array of code lengths.
+ ///
+ ///
+ /// the array of code lengths
+ ///
+ public InflaterHuffmanTree(byte[] codeLengths)
+ {
+ BuildTree(codeLengths);
+ }
+ #endregion
+
+ 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);
+ }
+ }
+
+ /* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g
+ if (code != 65536)
+ {
+ throw new SharpZipBaseException("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/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
new file mode 100644
index 000000000..42fa1753a
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
@@ -0,0 +1,326 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+ using System.IO;
+
+ //using ICSharpCode.SharpZipLib.Zip;
+ //using ICSharpCode.SharpZipLib.Zip.Compression;
+
+ ///
+ /// An input buffer customised for use by
+ ///
+ ///
+ /// The buffer supports decryption of incoming data.
+ ///
+ public class InflaterInputBuffer
+ {
+ #region Constructors
+ ///
+ /// Initialise a new instance of with a default buffer size
+ ///
+ /// The stream to buffer.
+ public InflaterInputBuffer(Stream stream) : this(stream, 4096)
+ {
+ }
+
+ ///
+ /// Initialise a new instance of
+ ///
+ /// The stream to buffer.
+ /// The size to use for the buffer
+ /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB.
+ public InflaterInputBuffer(Stream stream, int bufferSize)
+ {
+ inputStream = stream;
+ if (bufferSize < 1024)
+ {
+ bufferSize = 1024;
+ }
+ rawData = new byte[bufferSize];
+ clearText = rawData;
+ }
+ #endregion
+
+ ///
+ /// Get the length of bytes bytes in the
+ ///
+ public int RawLength
+ {
+ get
+ {
+ return rawLength;
+ }
+ }
+
+ ///
+ /// Get the contents of the raw data buffer.
+ ///
+ /// This may contain encrypted data.
+ public byte[] RawData
+ {
+ get
+ {
+ return rawData;
+ }
+ }
+
+ ///
+ /// Get the number of useable bytes in
+ ///
+ public int ClearTextLength
+ {
+ get
+ {
+ return clearTextLength;
+ }
+ }
+
+ ///
+ /// Get the contents of the clear text buffer.
+ ///
+ public byte[] ClearText
+ {
+ get
+ {
+ return clearText;
+ }
+ }
+
+ ///
+ /// Get/set the number of bytes available
+ ///
+ public int Available
+ {
+ get { return available; }
+ set { available = value; }
+ }
+
+ ///
+ /// Call passing the current clear text buffer contents.
+ ///
+ /// The inflater to set input for.
+ public void SetInflaterInput(Inflater inflater)
+ {
+ if (available > 0)
+ {
+ inflater.SetInput(clearText, clearTextLength - available, available);
+ available = 0;
+ }
+ }
+
+ ///
+ /// Fill the buffer from the underlying input stream.
+ ///
+ public void Fill()
+ {
+ rawLength = 0;
+ int toRead = rawData.Length;
+
+ while (toRead > 0)
+ {
+ int count = inputStream.Read(rawData, rawLength, toRead);
+ if (count <= 0)
+ {
+ break;
+ }
+ rawLength += count;
+ toRead -= count;
+ }
+
+#if !NETCF_1_0 && !NOCRYPTO
+ if (cryptoTransform != null)
+ {
+ clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
+ }
+ else
+#endif
+ {
+ clearTextLength = rawLength;
+ }
+
+ available = clearTextLength;
+ }
+
+ ///
+ /// Read a buffer directly from the input stream
+ ///
+ /// The buffer to fill
+ /// Returns the number of bytes read.
+ public int ReadRawBuffer(byte[] buffer)
+ {
+ return ReadRawBuffer(buffer, 0, buffer.Length);
+ }
+
+ ///
+ /// Read a buffer directly from the input stream
+ ///
+ /// The buffer to read into
+ /// The offset to start reading data into.
+ /// The number of bytes to read.
+ /// Returns the number of bytes read.
+ public int ReadRawBuffer(byte[] outBuffer, int offset, int length)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ int currentOffset = offset;
+ int currentLength = length;
+
+ while (currentLength > 0)
+ {
+ if (available <= 0)
+ {
+ Fill();
+ if (available <= 0)
+ {
+ return 0;
+ }
+ }
+ int toCopy = Math.Min(currentLength, available);
+ System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
+ currentOffset += toCopy;
+ currentLength -= toCopy;
+ available -= toCopy;
+ }
+ return length;
+ }
+
+ ///
+ /// Read clear text data from the input stream.
+ ///
+ /// The buffer to add data to.
+ /// The offset to start adding data at.
+ /// The number of bytes to read.
+ /// Returns the number of bytes actually read.
+ public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ int currentOffset = offset;
+ int currentLength = length;
+
+ while (currentLength > 0)
+ {
+ if (available <= 0)
+ {
+ Fill();
+ if (available <= 0)
+ {
+ return 0;
+ }
+ }
+
+ int toCopy = Math.Min(currentLength, available);
+ Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy);
+ currentOffset += toCopy;
+ currentLength -= toCopy;
+ available -= toCopy;
+ }
+ return length;
+ }
+
+ ///
+ /// Read a from the input stream.
+ ///
+ /// Returns the byte read.
+ public int ReadLeByte()
+ {
+ if (available <= 0)
+ {
+ Fill();
+ if (available <= 0)
+ {
+ throw new ImageFormatException("EOF in header");
+ }
+ }
+ byte result = rawData[rawLength - available];
+ available -= 1;
+ return result;
+ }
+
+ ///
+ /// Read an in little endian byte order.
+ ///
+ /// The short value read case to an int.
+ public int ReadLeShort()
+ {
+ return ReadLeByte() | (ReadLeByte() << 8);
+ }
+
+ ///
+ /// Read an in little endian byte order.
+ ///
+ /// The int value read.
+ public int ReadLeInt()
+ {
+ return ReadLeShort() | (ReadLeShort() << 16);
+ }
+
+ ///
+ /// Read a in little endian byte order.
+ ///
+ /// The long value read.
+ public long ReadLeLong()
+ {
+ return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
+ }
+
+#if !NETCF_1_0 && !NOCRYPTO
+ ///
+ /// Get/set the to apply to any data.
+ ///
+ /// Set this value to null to have no transform applied.
+ public ICryptoTransform CryptoTransform
+ {
+ set
+ {
+ cryptoTransform = value;
+ if (cryptoTransform != null)
+ {
+ if (rawData == clearText)
+ {
+ if (internalClearText == null)
+ {
+ internalClearText = new byte[rawData.Length];
+ }
+ clearText = internalClearText;
+ }
+ clearTextLength = rawLength;
+ if (available > 0)
+ {
+ cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
+ }
+ }
+ else
+ {
+ clearText = rawData;
+ clearTextLength = rawLength;
+ }
+ }
+ }
+#endif
+
+ #region Instance Fields
+ int rawLength;
+ byte[] rawData;
+
+ int clearTextLength;
+ byte[] clearText;
+#if !NETCF_1_0 && !NOCRYPTO
+ byte[] internalClearText;
+#endif
+
+ int available;
+
+#if !NETCF_1_0 && !NOCRYPTO
+ ICryptoTransform cryptoTransform;
+#endif
+ Stream inputStream;
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
new file mode 100644
index 000000000..62cba8bb4
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
@@ -0,0 +1,407 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+ using System.IO;
+
+ //using ICSharpCode.SharpZipLib;
+ //using ICSharpCode.SharpZipLib.Zip;
+ //using ICSharpCode.SharpZipLib.Zip.Compression;
+
+ ///
+ /// This filter stream is used to decompress data compressed using the "deflate"
+ /// format. The "deflate" format is described in 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
+ {
+ #region Constructors
+ ///
+ /// Create an InflaterInputStream with the default decompressor
+ /// and a default buffer size of 4KB.
+ ///
+ ///
+ /// The InputStream to read bytes from
+ ///
+ public InflaterInputStream(Stream baseInputStream)
+ : this(baseInputStream, new Inflater(), 4096)
+ {
+ }
+
+ ///
+ /// Create an InflaterInputStream with the specified decompressor
+ /// and a default buffer size of 4KB.
+ ///
+ ///
+ /// The source of input data
+ ///
+ ///
+ /// 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 decompressor and the specified buffer size.
+ ///
+ ///
+ /// The InputStream to read bytes from
+ ///
+ ///
+ /// The decompressor to use
+ ///
+ ///
+ /// Size of the buffer to use
+ ///
+ public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize)
+ {
+ if (baseInputStream == null)
+ {
+ throw new ArgumentNullException("baseInputStream");
+ }
+
+ if (inflater == null)
+ {
+ throw new ArgumentNullException("inflater");
+ }
+
+ if (bufferSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException("bufferSize");
+ }
+
+ this.baseInputStream = baseInputStream;
+ this.inf = inflater;
+
+ inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize);
+ }
+
+ #endregion
+
+ ///
+ /// Get/set flag indicating ownership of underlying stream.
+ /// When the flag is true will close the underlying stream also.
+ ///
+ ///
+ /// The default value is true.
+ ///
+ public bool IsStreamOwner
+ {
+ get { return isStreamOwner; }
+ set { isStreamOwner = value; }
+ }
+
+ ///
+ /// Skip specified number of bytes of uncompressed data
+ ///
+ ///
+ /// Number of bytes to skip
+ ///
+ ///
+ /// The number of bytes skipped, zero if the end of
+ /// stream has been reached
+ ///
+ ///
+ /// The number of bytes to skip is less than or equal to zero.
+ ///
+ public long Skip(long count)
+ {
+ if (count <= 0)
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+
+ // v0.80 Skip by seeking if underlying stream supports it...
+ if (baseInputStream.CanSeek)
+ {
+ baseInputStream.Seek(count, SeekOrigin.Current);
+ return count;
+ }
+ else
+ {
+ int length = 2048;
+ if (count < length)
+ {
+ length = (int)count;
+ }
+
+ byte[] tmp = new byte[length];
+ int readCount = 1;
+ long toSkip = count;
+
+ while ((toSkip > 0) && (readCount > 0))
+ {
+ if (toSkip < length)
+ {
+ length = (int)toSkip;
+ }
+
+ readCount = baseInputStream.Read(tmp, 0, length);
+ toSkip -= readCount;
+ }
+
+ return count - toSkip;
+ }
+ }
+
+ ///
+ /// Clear any cryptographic state.
+ ///
+ protected void StopDecrypting()
+ {
+#if !NETCF_1_0 && !NOCRYPTO
+ inputBuffer.CryptoTransform = null;
+#endif
+ }
+
+ ///
+ /// 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;
+ }
+ }
+
+ ///
+ /// Fills the buffer with more data to decompress.
+ ///
+ ///
+ /// Stream ends early
+ ///
+ protected void Fill()
+ {
+ // Protect against redundant calls
+ if (inputBuffer.Available <= 0)
+ {
+ inputBuffer.Fill();
+ if (inputBuffer.Available <= 0)
+ {
+ throw new ImageFormatException("Unexpected EOF");
+ }
+ }
+ inputBuffer.SetInflaterInput(inf);
+ }
+
+ #region Stream Overrides
+ ///
+ /// Gets a value indicating whether the current stream supports reading
+ ///
+ public override bool CanRead
+ {
+ get
+ {
+ return baseInputStream.CanRead;
+ }
+ }
+
+ ///
+ /// Gets a value of false indicating seeking is not supported for this stream.
+ ///
+ public override bool CanSeek
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value of false indicating that this stream is not writeable.
+ ///
+ public override bool CanWrite
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// A value representing the length of the stream in bytes.
+ ///
+ public override long Length
+ {
+ get
+ {
+ return inputBuffer.RawLength;
+ }
+ }
+
+ ///
+ /// The current position within the stream.
+ /// Throws a NotSupportedException when attempting to set the position
+ ///
+ /// Attempting to set the position
+ public override long Position
+ {
+ get
+ {
+ return baseInputStream.Position;
+ }
+ set
+ {
+ throw new NotSupportedException("InflaterInputStream Position not supported");
+ }
+ }
+
+ ///
+ /// Flushes the baseInputStream
+ ///
+ public override void Flush()
+ {
+ baseInputStream.Flush();
+ }
+
+ ///
+ /// Sets the position within the current stream
+ /// Always throws a NotSupportedException
+ ///
+ /// The relative offset to seek to.
+ /// The defining where to seek from.
+ /// The new position in the stream.
+ /// Any access
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException("Seek not supported");
+ }
+
+ ///
+ /// Set the length of the current stream
+ /// Always throws a NotSupportedException
+ ///
+ /// The new length value for the stream.
+ /// Any access
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("InflaterInputStream SetLength not supported");
+ }
+
+ ///
+ /// Writes a sequence of bytes to stream and advances the current position
+ /// This method always throws a NotSupportedException
+ ///
+ /// Thew buffer containing data to write.
+ /// The offset of the first byte to write.
+ /// The number of bytes to write.
+ /// Any access
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("InflaterInputStream Write not supported");
+ }
+
+ ///
+ /// Writes one byte to the current stream and advances the current position
+ /// Always throws a NotSupportedException
+ ///
+ /// The byte to write.
+ /// Any access
+ public override void WriteByte(byte value)
+ {
+ throw new NotSupportedException("InflaterInputStream WriteByte not supported");
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (disposing && !isClosed)
+ {
+ isClosed = true;
+ if (isStreamOwner)
+ {
+ baseInputStream.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Reads decompressed data into the provided buffer 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
+ ///
+ /// The number of bytes read. Zero signals the end of stream
+ ///
+ /// Inflater needs a dictionary
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (inf.IsNeedingDictionary)
+ {
+ throw new ImageFormatException("Need a dictionary");
+ }
+
+ int remainingBytes = count;
+ while (true)
+ {
+ int bytesRead = inf.Inflate(buffer, offset, remainingBytes);
+ offset += bytesRead;
+ remainingBytes -= bytesRead;
+
+ if (remainingBytes == 0 || inf.IsFinished)
+ {
+ break;
+ }
+
+ if (inf.IsNeedingInput)
+ {
+ Fill();
+ }
+ else if (bytesRead == 0)
+ {
+ throw new ImageFormatException("Dont know what to do");
+ }
+ }
+ return count - remainingBytes;
+ }
+ #endregion
+
+ #region Instance Fields
+ ///
+ /// Decompressor for this stream
+ ///
+ protected Inflater inf;
+
+ ///
+ /// Input buffer for this stream.
+ ///
+ protected InflaterInputBuffer inputBuffer;
+
+ ///
+ /// Base stream the inflater reads from.
+ ///
+ private Stream baseInputStream;
+
+ ///
+ /// The compressed size
+ ///
+ protected long csize;
+
+ ///
+ /// Flag indicating wether this instance has been closed or not.
+ ///
+ bool isClosed;
+
+ ///
+ /// Flag indicating wether this instance is designated the stream owner.
+ /// When closing if this flag is true the underlying stream is closed.
+ ///
+ bool isStreamOwner = true;
+ #endregion
+ }
+}
+
diff --git a/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs b/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs
new file mode 100644
index 000000000..353bebadd
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs
@@ -0,0 +1,217 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// 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
+ {
+ #region Constants
+ const int WindowSize = 1 << 15;
+ const int WindowMask = WindowSize - 1;
+ #endregion
+
+ #region Instance Fields
+ byte[] window = new byte[WindowSize]; //The window is 2^15 bytes
+ int windowEnd;
+ int windowFilled;
+ #endregion
+
+ ///
+ /// Write a byte to this output window
+ ///
+ /// value to write
+ ///
+ /// if window is full
+ ///
+ public void Write(int value)
+ {
+ if (windowFilled++ == WindowSize)
+ {
+ throw new InvalidOperationException("Window full");
+ }
+ window[windowEnd++] = (byte)value;
+ windowEnd &= WindowMask;
+ }
+
+
+ private void SlowRepeat(int repStart, int length, int distance)
+ {
+ while (length-- > 0)
+ {
+ window[windowEnd++] = window[repStart++];
+ windowEnd &= WindowMask;
+ repStart &= WindowMask;
+ }
+ }
+
+ ///
+ /// Append a byte pattern already in the window itself
+ ///
+ /// length of pattern to copy
+ /// distance from end of window pattern occurs
+ ///
+ /// If the repeated data overflows the window
+ ///
+ public void Repeat(int length, int distance)
+ {
+ if ((windowFilled += length) > WindowSize)
+ {
+ throw new InvalidOperationException("Window full");
+ }
+
+ int repStart = (windowEnd - distance) & WindowMask;
+ int border = WindowSize - length;
+ if ((repStart <= border) && (windowEnd < border))
+ {
+ if (length <= distance)
+ {
+ System.Array.Copy(window, repStart, window, windowEnd, length);
+ windowEnd += length;
+ }
+ else
+ {
+ // We have to copy manually, since the repeat pattern overlaps.
+ while (length-- > 0)
+ {
+ window[windowEnd++] = window[repStart++];
+ }
+ }
+ }
+ else
+ {
+ SlowRepeat(repStart, length, distance);
+ }
+ }
+
+ ///
+ /// Copy from input manipulator to internal window
+ ///
+ /// source of data
+ /// length of data to copy
+ /// the number of bytes copied
+ public int CopyStored(StreamManipulator input, int length)
+ {
+ length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes);
+ int copied;
+
+ int tailLen = WindowSize - windowEnd;
+ if (length > tailLen)
+ {
+ copied = input.CopyBytes(window, windowEnd, tailLen);
+ if (copied == tailLen)
+ {
+ copied += input.CopyBytes(window, 0, length - tailLen);
+ }
+ }
+ else
+ {
+ copied = input.CopyBytes(window, windowEnd, length);
+ }
+
+ windowEnd = (windowEnd + copied) & WindowMask;
+ windowFilled += copied;
+ return copied;
+ }
+
+ ///
+ /// Copy dictionary to window
+ ///
+ /// source dictionary
+ /// offset of start in source dictionary
+ /// length of dictionary
+ ///
+ /// If window isnt empty
+ ///
+ public void CopyDict(byte[] dictionary, int offset, int length)
+ {
+ if (dictionary == null)
+ {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ if (windowFilled > 0)
+ {
+ throw new InvalidOperationException();
+ }
+
+ if (length > WindowSize)
+ {
+ offset += length - WindowSize;
+ length = WindowSize;
+ }
+ System.Array.Copy(dictionary, offset, window, 0, length);
+ windowEnd = length & WindowMask;
+ }
+
+ ///
+ /// Get remaining unfilled space in window
+ ///
+ /// Number of bytes left in window
+ public int GetFreeSpace()
+ {
+ return WindowSize - windowFilled;
+ }
+
+ ///
+ /// Get bytes available for output in window
+ ///
+ /// Number of bytes filled
+ public int GetAvailable()
+ {
+ return windowFilled;
+ }
+
+ ///
+ /// Copy contents of window to output
+ ///
+ /// buffer to copy to
+ /// offset to start at
+ /// number of bytes to count
+ /// The number of bytes copied
+ ///
+ /// If a window underflow occurs
+ ///
+ public int CopyOutput(byte[] output, int offset, int len)
+ {
+ int copyEnd = windowEnd;
+ if (len > windowFilled)
+ {
+ len = windowFilled;
+ }
+ else
+ {
+ copyEnd = (windowEnd - windowFilled + len) & WindowMask;
+ }
+
+ int copied = len;
+ int tailLen = len - copyEnd;
+
+ if (tailLen > 0)
+ {
+ System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
+ offset += tailLen;
+ len = copyEnd;
+ }
+ System.Array.Copy(window, copyEnd - len, output, offset, len);
+ windowFilled -= copied;
+ if (windowFilled < 0)
+ {
+ throw new InvalidOperationException();
+ }
+ return copied;
+ }
+
+ ///
+ /// Reset by clearing window so GetAvailable returns 0
+ ///
+ public void Reset()
+ {
+ windowFilled = windowEnd = 0;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs
new file mode 100644
index 000000000..02916ae3d
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs
@@ -0,0 +1,263 @@
+namespace ImageProcessor.Formats
+{
+ ///
+ /// 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
+ {
+ #region Instance Fields
+ ///
+ /// Internal work buffer
+ ///
+ byte[] buffer_;
+
+ int start;
+ int end;
+
+ uint bits;
+ int bitCount;
+ #endregion
+
+ #region Constructors
+ ///
+ /// construct instance using default buffer size of 4096
+ ///
+ public PendingBuffer() : this(4096)
+ {
+ }
+
+ ///
+ /// construct instance using specified buffer size
+ ///
+ ///
+ /// size to use for internal buffer
+ ///
+ public PendingBuffer(int bufferSize)
+ {
+ buffer_ = new byte[bufferSize];
+ }
+
+ #endregion
+
+ ///
+ /// Clear internal state/buffers
+ ///
+ public void Reset()
+ {
+ start = end = bitCount = 0;
+ }
+
+ ///
+ /// Write a byte to buffer
+ ///
+ ///
+ /// The value to write
+ ///
+ public void WriteByte(int value)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ buffer_[end++] = unchecked((byte)value);
+ }
+
+ ///
+ /// Write a short value to buffer LSB first
+ ///
+ ///
+ /// The value to write.
+ ///
+ public void WriteShort(int value)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ buffer_[end++] = unchecked((byte)value);
+ buffer_[end++] = unchecked((byte)(value >> 8));
+ }
+
+ ///
+ /// write an integer LSB first
+ ///
+ /// The value to write.
+ public void WriteInt(int value)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ buffer_[end++] = unchecked((byte)value);
+ buffer_[end++] = unchecked((byte)(value >> 8));
+ buffer_[end++] = unchecked((byte)(value >> 16));
+ buffer_[end++] = unchecked((byte)(value >> 24));
+ }
+
+ ///
+ /// Write a block of data to buffer
+ ///
+ /// data to write
+ /// offset of first byte to write
+ /// number of bytes to write
+ public void WriteBlock(byte[] block, int offset, int length)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ System.Array.Copy(block, offset, buffer_, end, length);
+ end += length;
+ }
+
+ ///
+ /// The number of bits written to the buffer
+ ///
+ public int BitCount
+ {
+ get
+ {
+ return bitCount;
+ }
+ }
+
+ ///
+ /// Align internal buffer on a byte boundary
+ ///
+ public void AlignToByte()
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ if (bitCount > 0)
+ {
+ buffer_[end++] = unchecked((byte)bits);
+ if (bitCount > 8)
+ {
+ buffer_[end++] = unchecked((byte)(bits >> 8));
+ }
+ }
+ bits = 0;
+ bitCount = 0;
+ }
+
+ ///
+ /// Write bits to internal buffer
+ ///
+ /// source of bits
+ /// number of bits to write
+ public void WriteBits(int b, int count)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+
+ // if (DeflaterConstants.DEBUGGING) {
+ // //Console.WriteLine("writeBits("+b+","+count+")");
+ // }
+#endif
+ bits |= (uint)(b << bitCount);
+ bitCount += count;
+ if (bitCount >= 16)
+ {
+ buffer_[end++] = unchecked((byte)bits);
+ buffer_[end++] = unchecked((byte)(bits >> 8));
+ bits >>= 16;
+ bitCount -= 16;
+ }
+ }
+
+ ///
+ /// Write a short value to internal buffer most significant byte first
+ ///
+ /// value to write
+ public void WriteShortMSB(int s)
+ {
+#if DebugDeflation
+ if (DeflaterConstants.DEBUGGING && (start != 0) )
+ {
+ throw new SharpZipBaseException("Debug check: start != 0");
+ }
+#endif
+ buffer_[end++] = unchecked((byte)(s >> 8));
+ buffer_[end++] = unchecked((byte)s);
+ }
+
+ ///
+ /// Indicates if buffer has been flushed
+ ///
+ 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.
+ /// The maximum number of bytes to store.
+ /// The number of bytes flushed.
+ public int Flush(byte[] output, int offset, int length)
+ {
+ if (bitCount >= 8)
+ {
+ buffer_[end++] = unchecked((byte)bits);
+ bits >>= 8;
+ bitCount -= 8;
+ }
+
+ if (length > end - start)
+ {
+ length = end - start;
+ System.Array.Copy(buffer_, start, output, offset, length);
+ start = 0;
+ end = 0;
+ }
+ else
+ {
+ System.Array.Copy(buffer_, start, output, offset, length);
+ start += length;
+ }
+ return length;
+ }
+
+ ///
+ /// Convert internal buffer to byte array.
+ /// Buffer is empty on completion
+ ///
+ ///
+ /// The internal buffer contents converted to a byte array.
+ ///
+ public byte[] ToByteArray()
+ {
+ byte[] result = new byte[end - start];
+ System.Array.Copy(buffer_, start, result, 0, result.Length);
+ start = 0;
+ end = 0;
+ return result;
+ }
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs b/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs
new file mode 100644
index 000000000..1f934558d
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs
@@ -0,0 +1,279 @@
+namespace ImageProcessor.Formats
+{
+ using System;
+
+ ///
+ /// This class allows us to retrieve a specified number 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 than 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
+ {
+ #region Constructors
+ ///
+ /// Constructs a default StreamManipulator with all buffers empty
+ ///
+ public StreamManipulator()
+ {
+ }
+ #endregion
+
+ ///
+ /// Get the next sequence of bits but don't increase input pointer. bitCount must be
+ /// less or equal 16 and if this call succeeds, you must drop
+ /// at least n - 8 bits in the next call.
+ ///
+ /// The number of bits to peek.
+ ///
+ /// the value of the bits, or -1 if not enough bits available. */
+ ///
+ public int PeekBits(int bitCount)
+ {
+ if (bitsInBuffer_ < bitCount)
+ {
+ if (windowStart_ == windowEnd_)
+ {
+ return -1; // ok
+ }
+ buffer_ |= (uint)((window_[windowStart_++] & 0xff |
+ (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_);
+ bitsInBuffer_ += 16;
+ }
+ return (int)(buffer_ & ((1 << bitCount) - 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.
+ ///
+ /// The number of bits to drop.
+ public void DropBits(int bitCount)
+ {
+ buffer_ >>= bitCount;
+ bitsInBuffer_ -= bitCount;
+ }
+
+ ///
+ /// Gets the next n bits and increases input pointer. This is equivalent
+ /// to followed by , except for correct error handling.
+ ///
+ /// The number of bits to retrieve.
+ ///
+ /// the value of the bits, or -1 if not enough bits available.
+ ///
+ public int GetBits(int bitCount)
+ {
+ int bits = PeekBits(bitCount);
+ if (bits >= 0)
+ {
+ DropBits(bitCount);
+ }
+ 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 bitsInBuffer_;
+ }
+ }
+
+ ///
+ /// Gets the number of bytes available.
+ ///
+ ///
+ /// The number of bytes available.
+ ///
+ public int AvailableBytes
+ {
+ get
+ {
+ return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3);
+ }
+ }
+
+ ///
+ /// Skips to the next byte boundary.
+ ///
+ public void SkipToByteBoundary()
+ {
+ buffer_ >>= (bitsInBuffer_ & 7);
+ bitsInBuffer_ &= ~7;
+ }
+
+ ///
+ /// Returns true when SetInput can be called
+ ///
+ public bool IsNeedingInput
+ {
+ get
+ {
+ return windowStart_ == windowEnd_;
+ }
+ }
+
+ ///
+ /// Copies 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 to copy bytes to.
+ ///
+ ///
+ /// The offset in the buffer at which copying starts
+ ///
+ ///
+ /// The length to copy, 0 is allowed.
+ ///
+ ///
+ /// The number of bytes copied, 0 if no bytes were available.
+ ///
+ ///
+ /// Length is less than zero
+ ///
+ ///
+ /// Bit buffer isnt byte aligned
+ ///
+ public int CopyBytes(byte[] output, int offset, int length)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException("length");
+ }
+
+ if ((bitsInBuffer_ & 7) != 0)
+ {
+ // bits_in_buffer may only be 0 or a multiple of 8
+ throw new InvalidOperationException("Bit buffer is not byte aligned!");
+ }
+
+ int count = 0;
+ while ((bitsInBuffer_ > 0) && (length > 0))
+ {
+ output[offset++] = (byte)buffer_;
+ buffer_ >>= 8;
+ bitsInBuffer_ -= 8;
+ length--;
+ count++;
+ }
+
+ if (length == 0)
+ {
+ return count;
+ }
+
+ int avail = windowEnd_ - windowStart_;
+ if (length > avail)
+ {
+ length = avail;
+ }
+ System.Array.Copy(window_, windowStart_, output, offset, length);
+ windowStart_ += length;
+
+ if (((windowStart_ - windowEnd_) & 1) != 0)
+ {
+ // We always want an even number of bytes in input, see peekBits
+ buffer_ = (uint)(window_[windowStart_++] & 0xff);
+ bitsInBuffer_ = 8;
+ }
+ return count + length;
+ }
+
+ ///
+ /// Resets state and empties internal buffers
+ ///
+ public void Reset()
+ {
+ buffer_ = 0;
+ windowStart_ = windowEnd_ = bitsInBuffer_ = 0;
+ }
+
+ ///
+ /// Add more input for consumption.
+ /// Only call when IsNeedingInput returns true
+ ///
+ /// data to be input
+ /// offset of first byte of input
+ /// number of bytes of input to add.
+ public void SetInput(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+
+ if (offset < 0)
+ {
+#if NETCF_1_0
+ throw new ArgumentOutOfRangeException("offset");
+#else
+ throw new ArgumentOutOfRangeException("offset", "Cannot be negative");
+#endif
+ }
+
+ if (count < 0)
+ {
+#if NETCF_1_0
+ throw new ArgumentOutOfRangeException("count");
+#else
+ throw new ArgumentOutOfRangeException("count", "Cannot be negative");
+#endif
+ }
+
+ if (windowStart_ < windowEnd_)
+ {
+ throw new InvalidOperationException("Old input was not completely processed");
+ }
+
+ int end = offset + count;
+
+ // We want to throw an ArrayIndexOutOfBoundsException early.
+ // Note the check also handles integer wrap around.
+ if ((offset > end) || (end > buffer.Length))
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+
+ if ((count & 1) != 0)
+ {
+ // We always want an even number of bytes in input, see PeekBits
+ buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_);
+ bitsInBuffer_ += 8;
+ }
+
+ window_ = buffer;
+ windowStart_ = offset;
+ windowEnd_ = end;
+ }
+
+ #region Instance Fields
+ private byte[] window_;
+ private int windowStart_;
+ private int windowEnd_;
+
+ private uint buffer_;
+ private int bitsInBuffer_;
+ #endregion
+ }
+}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
new file mode 100644
index 000000000..6dccc6a96
--- /dev/null
+++ b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
@@ -0,0 +1,329 @@
+namespace ImageProcessor.Formats
+{
+ using System.Text;
+
+ ///
+ /// This class contains constants used for Zip format files
+ ///
+ public static class ZipConstants
+ {
+ #region Versions
+ ///
+ /// The version made by field for entries in the central header when created by this library
+ ///
+ ///
+ /// This is also the Zip version for the library when comparing against the version required to extract
+ /// for an entry.
+ public const int VersionMadeBy = 51; // was 45 before AES
+
+ ///
+ /// The minimum version required to support strong encryption
+ ///
+ public const int VersionStrongEncryption = 50;
+
+ ///
+ /// Version indicating AES encryption
+ ///
+ public const int VERSION_AES = 51;
+
+ ///
+ /// The version required for Zip64 extensions (4.5 or higher)
+ ///
+ public const int VersionZip64 = 45;
+ #endregion
+
+ #region Header Sizes
+ ///
+ /// Size of local entry header (excluding variable length fields at end)
+ ///
+ public const int LocalHeaderBaseSize = 30;
+
+ ///
+ /// Size of Zip64 data descriptor
+ ///
+ public const int Zip64DataDescriptorSize = 24;
+
+ ///
+ /// Size of data descriptor
+ ///
+ public const int DataDescriptorSize = 16;
+
+ ///
+ /// Size of central header entry (excluding variable fields)
+ ///
+ public const int CentralHeaderBaseSize = 46;
+
+ ///
+ /// Size of end of central record (excluding variable fields)
+ ///
+ public const int EndOfCentralRecordBaseSize = 22;
+
+ ///
+ /// Size of 'classic' cryptographic header stored before any entry data
+ ///
+ public const int CryptoHeaderSize = 12;
+ #endregion
+
+ #region Header Signatures
+
+ ///
+ /// Signature for local entry header
+ ///
+ public const int LocalHeaderSignature = 'P' | ('K' << 8) | (3 << 16) | (4 << 24);
+
+ ///
+ /// Signature for spanning entry
+ ///
+ public const int SpanningSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24);
+
+ ///
+ /// Signature for temporary spanning entry
+ ///
+ public const int SpanningTempSignature = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24);
+
+ ///
+ /// Signature for data descriptor
+ ///
+ ///
+ /// This is only used where the length, Crc, or compressed size isnt known when the
+ /// entry is created and the output stream doesnt support seeking.
+ /// The local entry cannot be 'patched' with the correct values in this case
+ /// so the values are recorded after the data prefixed by this header, as well as in the central directory.
+ ///
+ public const int DataDescriptorSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24);
+
+ ///
+ /// Signature for central header
+ ///
+ public const int CentralHeaderSignature = 'P' | ('K' << 8) | (1 << 16) | (2 << 24);
+
+ ///
+ /// Signature for Zip64 central file header
+ ///
+ public const int Zip64CentralFileHeaderSignature = 'P' | ('K' << 8) | (6 << 16) | (6 << 24);
+
+ ///
+ /// Signature for Zip64 central directory locator
+ ///
+ public const int Zip64CentralDirLocatorSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24);
+
+ ///
+ /// Signature for archive extra data signature (were headers are encrypted).
+ ///
+ public const int ArchiveExtraDataSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24);
+
+ ///
+ /// Central header digitial signature
+ ///
+ public const int CentralHeaderDigitalSignature = 'P' | ('K' << 8) | (5 << 16) | (5 << 24);
+
+ ///
+ /// End of central directory record signature
+ ///
+ public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24);
+
+ #endregion
+
+#if NETCF_1_0 || NETCF_2_0
+ // This isnt so great but is better than nothing.
+ // Trying to work out an appropriate OEM code page would be good.
+ // 850 is a good default for english speakers particularly in Europe.
+ static int defaultCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
+#elif PCL
+ static Encoding defaultEncoding = Encoding.UTF8;
+#else
+ ///
+ /// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc.
+ /// But sometimes it yields the special value of 1 which is nicknamed CodePageNoOEM in sources (might also mean CP_OEMCP, but Encoding puts it so).
+ /// This was observed on Ukranian and Hindu systems.
+ /// Given this value, throws an .
+ /// So replace it with some fallback, e.g. 437 which is the default cpcp in a console in a default Windows installation.
+ ///
+ static int defaultCodePage =
+ // these values cause ArgumentException in subsequent calls to Encoding::GetEncoding()
+ ((Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 1) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 2) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 3) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 42))
+ ? 437 // The default OEM encoding in a console in a default Windows installation, as a fallback.
+ : Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage;
+#endif
+#if !PCL
+ ///
+ /// Default encoding used for string conversion. 0 gives the default system OEM code page.
+ /// Dont use unicode encodings if you want to be Zip compatible!
+ /// Using the default code page isnt the full solution neccessarily
+ /// there are many variable factors, codepage 850 is often a good choice for
+ /// European users, however be careful about compatability.
+ ///
+ public static int DefaultCodePage {
+ get {
+ return defaultCodePage;
+ }
+ set {
+ if ((value < 0) || (value > 65535) ||
+ (value == 1) || (value == 2) || (value == 3) || (value == 42)) {
+ throw new ArgumentOutOfRangeException("value");
+ }
+
+ defaultCodePage = value;
+ }
+ }
+#else
+ ///
+ /// PCL don't support CodePage so we used Encoding instead of
+ ///
+ public static Encoding DefaultEncoding
+ {
+ get
+ {
+ return defaultEncoding;
+ }
+ set
+ {
+ defaultEncoding = value;
+ }
+ }
+#endif
+
+ ///
+ /// Convert a portion of a byte array to a string.
+ ///
+ ///
+ /// Data to convert to string
+ ///
+ ///
+ /// Number of bytes to convert starting from index 0
+ ///
+ ///
+ /// data[0]..data[count - 1] converted to a string
+ ///
+ public static string ConvertToString(byte[] data, int count)
+ {
+ if (data == null)
+ {
+ return string.Empty;
+ }
+#if !PCL
+ return Encoding.GetEncoding(DefaultCodePage).GetString(data, 0, count);
+#else
+ return DefaultEncoding.GetString(data, 0, count);
+#endif
+ }
+
+ ///
+ /// Convert a byte array to string
+ ///
+ ///
+ /// Byte array to convert
+ ///
+ ///
+ /// dataconverted to a string
+ ///
+ public static string ConvertToString(byte[] data)
+ {
+ if (data == null)
+ {
+ return string.Empty;
+ }
+ return ConvertToString(data, data.Length);
+ }
+
+ ///
+ /// Convert a byte array to string
+ ///
+ /// The applicable general purpose bits flags
+ ///
+ /// Byte array to convert
+ ///
+ /// The number of bytes to convert.
+ ///
+ /// dataconverted to a string
+ ///
+ public static string ConvertToStringExt(int flags, byte[] data, int count)
+ {
+ if (data == null)
+ {
+ return string.Empty;
+ }
+
+ if ((flags & (int)GeneralBitFlags.UnicodeText) != 0)
+ {
+ return Encoding.UTF8.GetString(data, 0, count);
+ }
+ else
+ {
+ return ConvertToString(data, count);
+ }
+ }
+
+ ///
+ /// Convert a byte array to string
+ ///
+ ///
+ /// Byte array to convert
+ ///
+ /// The applicable general purpose bits flags
+ ///
+ /// dataconverted to a string
+ ///
+ public static string ConvertToStringExt(int flags, byte[] data)
+ {
+ if (data == null)
+ {
+ return string.Empty;
+ }
+
+ if ((flags & (int)GeneralBitFlags.UnicodeText) != 0)
+ {
+ return Encoding.UTF8.GetString(data, 0, data.Length);
+ }
+ else
+ {
+ return ConvertToString(data, data.Length);
+ }
+ }
+
+ ///
+ /// Convert a string to a byte array
+ ///
+ ///
+ /// String to convert to an array
+ ///
+ /// Converted array
+ public static byte[] ConvertToArray(string str)
+ {
+ if (str == null)
+ {
+ return new byte[0];
+ }
+#if !PCL
+ return Encoding.GetEncoding(DefaultCodePage).GetBytes(str);
+#else
+ return DefaultEncoding.GetBytes(str);
+#endif
+ }
+
+ ///
+ /// Convert a string to a byte array
+ ///
+ /// The applicable general purpose bits flags
+ ///
+ /// String to convert to an array
+ ///
+ /// Converted array
+ public static byte[] ConvertToArray(int flags, string str)
+ {
+ if (str == null)
+ {
+ return new byte[0];
+ }
+
+ if ((flags & (int)GeneralBitFlags.UnicodeText) != 0)
+ {
+ return Encoding.UTF8.GetBytes(str);
+ }
+ else
+ {
+ return ConvertToArray(str);
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index a35f2f0a3..7d32368e7 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -17,14 +17,15 @@
v4.5
..\..\
true
- cfa9b76b
+
+
true
full
false
bin\Debug\
- DEBUG;TRACE
+ TRACE;DEBUG;NOCRYPTO;PCL
prompt
4
bin\Debug\ImageProcessor.XML
@@ -40,6 +41,9 @@
+
+
+
@@ -57,6 +61,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -211,12 +235,6 @@
-
-
- ..\..\packages\SharpZipLib.Portable.0.86.0.0002\lib\portable-net45+netcore45+wp8+win8+wpa81+MonoTouch+MonoAndroid\ICSharpCode.SharpZipLib.Portable.dll
- True
-
-
@@ -227,18 +245,15 @@
-
-
-
-
+
+
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-