diff --git a/src/ImageProcessor/Formats/Gif/LzwDecoder.cs b/src/ImageProcessor/Formats/Gif/LzwDecoder.cs index ce28d36d4..347b0e847 100644 --- a/src/ImageProcessor/Formats/Gif/LzwDecoder.cs +++ b/src/ImageProcessor/Formats/Gif/LzwDecoder.cs @@ -69,8 +69,8 @@ namespace ImageProcessor.Formats int availableCode = clearCode + 2; // Jillzhangs Code (Not From Me) see: http://giflib.codeplex.com/ - // TODO: It's imperative that this close is cleaned up and commented properly. - // TODO: Unfortunately I can't figure out the character encoding to translate from the original Chinese. + // TODO: It's imperative that this code is cleaned up and commented properly. + // TODO: Unfortunately I can't figure out the character encoding to translate from the original Chinese. int code; // ÓÃÓÚ´æ´¢µ±Ç°µÄ±àÂëÖµ int oldCode = NullCode; // ÓÃÓÚ´æ´¢ÉÏÒ»´ÎµÄ±àÂëÖµ int codeMask = (1 << codeSize) - 1; // ±íʾ±àÂëµÄ×î´óÖµ£¬Èç¹ûcodeSize=5,Ôòcode_mask=31 diff --git a/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs index 0b9a54dea..7ed887c25 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; @@ -6,7 +11,8 @@ /// 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) /// @@ -45,37 +51,34 @@ /// 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; + private const uint Base = 65521; /// - /// Returns the Adler32 data checksum computed so far. + /// The checksum calculated to far. /// - public long Value - { - get - { - return this.checksum; - } - } + private uint 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. + /// Initializes a new instance of the class. The checksum starts off with a value of 1. /// public Adler32() { this.Reset(); } + /// + /// Returns the Adler32 data checksum computed so far. + /// + public long Value => this.checksum; + /// /// Resets the Adler32 checksum to the initial value. /// @@ -97,8 +100,8 @@ uint s1 = this.checksum & 0xFFFF; uint s2 = this.checksum >> 16; - s1 = (s1 + ((uint)value & 0xFF)) % BASE; - s2 = (s1 + s2) % BASE; + s1 = (s1 + ((uint)value & 0xFF)) % Base; + s2 = (s1 + s2) % Base; this.checksum = (s2 << 16) + s1; } @@ -113,7 +116,7 @@ { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } this.Update(buffer, 0, buffer.Length); @@ -135,30 +138,30 @@ { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { - throw new ArgumentOutOfRangeException("offset", "cannot be negative"); + throw new ArgumentOutOfRangeException(nameof(offset), "cannot be negative"); } if (count < 0) { - throw new ArgumentOutOfRangeException("count", "cannot be negative"); + throw new ArgumentOutOfRangeException(nameof(count), "cannot be negative"); } if (offset >= buffer.Length) { - throw new ArgumentOutOfRangeException("offset", "not a valid index into buffer"); + throw new ArgumentOutOfRangeException(nameof(offset), "not a valid index into buffer"); } if (offset + count > buffer.Length) { - throw new ArgumentOutOfRangeException("count", "exceeds buffer size"); + throw new ArgumentOutOfRangeException(nameof(count), "exceeds buffer size"); } - //(By Per Bothner) + // (By Per Bothner) uint s1 = this.checksum & 0xFFFF; uint s2 = this.checksum >> 16; @@ -172,21 +175,19 @@ { n = count; } + count -= n; while (--n >= 0) { s1 = s1 + (uint)(buffer[offset++] & 0xff); s2 = s2 + s1; } - s1 %= BASE; - s2 %= BASE; + + 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 index f476143fe..f069b0fbf 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs @@ -10,7 +10,8 @@ namespace ImageProcessor.Formats /// /// 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 @@ -30,12 +31,19 @@ namespace ImageProcessor.Formats /// 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; + /// + /// The crc seed + /// + private const uint CrcSeed = 0xFFFFFFFF; - readonly static uint[] CrcTable = new uint[] { + /// + /// The table of all possible eight bit values for fast lookup. + /// + private static readonly uint[] CrcTable = + { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, @@ -90,15 +98,10 @@ namespace ImageProcessor.Formats 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; + private uint crc; /// /// Returns the CRC32 data checksum computed so far. @@ -107,8 +110,9 @@ namespace ImageProcessor.Formats { get { - return (long)this.crc; + return this.crc; } + set { this.crc = (uint)value; @@ -126,9 +130,7 @@ namespace ImageProcessor.Formats /// /// Updates the checksum with the int bval. /// - /// - /// the byte is taken as the lower 8 bits of value - /// + /// The byte is taken as the lower 8 bits of value. public void Update(int value) { this.crc ^= CrcSeed; @@ -146,7 +148,7 @@ namespace ImageProcessor.Formats { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } this.Update(buffer, 0, buffer.Length); @@ -168,17 +170,17 @@ namespace ImageProcessor.Formats { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } if (count < 0) { - throw new ArgumentOutOfRangeException("count", "Count cannot be less than zero"); + throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be less than zero"); } if (offset < 0 || offset + count > buffer.Length) { - throw new ArgumentOutOfRangeException("offset"); + throw new ArgumentOutOfRangeException(nameof(offset)); } this.crc ^= CrcSeed; @@ -190,5 +192,16 @@ namespace ImageProcessor.Formats this.crc ^= CrcSeed; } + + /// + /// Computes the crc value for the given byte. + /// + /// The previous value. + /// The byte to compute against. + /// The + internal static uint ComputeCrc32(uint oldCrc, byte value) + { + return CrcTable[(oldCrc ^ value) & 0xFF] ^ (oldCrc >> 8); + } } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs index 31a913609..479fe99a8 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { /// /// Strategies for deflater diff --git a/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs index cbabedd90..d1a35483d 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs @@ -1,9 +1,12 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +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 @@ -11,12 +14,11 @@ /// /// 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: * @@ -51,149 +53,205 @@ * (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; + public const int BestCompression = 9; /// /// The worst but fastest compression level. /// - public const int BEST_SPEED = 1; + public const int BestSpeed = 1; /// /// The default compression level. /// - public const int DEFAULT_COMPRESSION = -1; + public const int DefaultCompression = -1; /// /// This level won't compress at all but output uncompressed blocks. /// - public const int NO_COMPRESSION = 0; + public const int NoCompression = 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) - { + public const int Deflated = 8; + + /// + /// The is dictionary set flag. + /// + private const int IsSetdict = 0x01; + + /// + /// Flags whether flushing. + /// + private const int IsFlushing = 0x04; + + /// + /// Flags whether finishing. + /// + private const int IsFinishing = 0x08; + + /// + /// The initial stat flag + /// + private const int InitState = 0x00; + + /// + /// Flags setting the dictionary. + /// + private const int SetdictState = 0x01; + + /// + /// The busy state flag. + /// + private const int BusyState = 0x10; + + /// + /// The flushing state flag. + /// + private const int FlushingState = 0x14; + + /// + /// The finishing state flag. + /// + private const int FinishingState = 0x1c; + /// + /// The finished state flag. + /// + private const int FinishedState = 0x1e; + + /// + /// The closed state flag. + /// + private const int ClosedState = 0x7f; + + /// + /// The pending output. + /// + private readonly DeflaterPending pending; + + /// + /// If true no Zlib/RFC1950 headers or footers are generated + /// + private readonly bool noZlibHeaderOrFooter; + + /// + /// The deflater engine. + /// + private readonly DeflaterEngine engine; + + /// + /// Compression level. + /// + private int deflaterLevel; + + /// + /// The current state. + /// + private int state; + + /// + /// The total bytes of output written. + /// + private long totalOut; + + /// + /// Initializes a new instance of the class with the default compression level. + /// + public Deflater() + : this(DefaultCompression, false) + { } /// - /// Creates a new deflater with given compression level. + /// Initializes a new instance of the class with the given compressin level. /// /// - /// the compression level, a value between NO_COMPRESSION - /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + /// The compression level, a value between NoCompression and BestCompression, or DefaultCompression. /// - /// if lvl is out of range. - public Deflater(int level) : this(level, false) + /// If level is out of range. + public Deflater(int level) + : this(level, false) { - } /// - /// Creates a new deflater with given compression level. + /// Initializes a new instance of the class with the given compressin level. /// /// - /// the compression level, a value between NO_COMPRESSION - /// and BEST_COMPRESSION. + /// The compression level, a value between NoCompression and BestCompression, or DefaultCompression. /// /// - /// true, if we should suppress the Zlib/RFC1950 header at the + /// 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) + if (level == DefaultCompression) { level = 6; } - else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + else if (level < NoCompression || level > BestCompression) { - throw new ArgumentOutOfRangeException("level"); + throw new ArgumentOutOfRangeException(nameof(level)); } - pending = new DeflaterPending(); - engine = new DeflaterEngine(pending); + this.pending = new DeflaterPending(); + this.engine = new DeflaterEngine(this.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(); + this.SetStrategy(DeflateStrategy.Default); + this.SetLevel(level); + this.Reset(); } /// /// Gets the current adler checksum of the data that was processed so far. /// - public int Adler - { - get - { - return engine.Adler; - } - } + public int Adler => this.engine.Adler; /// /// Gets the number of input bytes processed so far. /// - public long TotalIn - { - get - { - return engine.TotalIn; - } - } + public long TotalIn => this.engine.TotalIn; /// /// Gets the number of output bytes so far. /// - public long TotalOut + public long TotalOut => this.totalOut; + + /// + /// Returns true if the stream was finished and no more output bytes + /// are available. + /// + public bool IsFinished => (this.state == FinishedState) && this.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 => this.engine.NeedsInput(); + + /// + /// 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() { - get - { - return totalOut; - } + this.state = this.noZlibHeaderOrFooter ? BusyState : InitState; + this.totalOut = 0; + this.pending.Reset(); + this.engine.Reset(); } /// @@ -205,7 +263,7 @@ /// public void Flush() { - state |= IS_FLUSHING; + this.state |= IsFlushing; } /// @@ -215,33 +273,7 @@ /// 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(); - } + this.state |= IsFlushing | IsFinishing; } /// @@ -261,7 +293,7 @@ /// public void SetInput(byte[] input) { - SetInput(input, 0, input.Length); + this.SetInput(input, 0, input.Length); } /// @@ -284,11 +316,12 @@ /// public void SetInput(byte[] input, int offset, int count) { - if ((state & IS_FINISHING) != 0) + if ((this.state & IsFinishing) != 0) { throw new InvalidOperationException("Finish() already called"); } - engine.SetInput(input, offset, count); + + this.engine.SetInput(input, offset, count); } /// @@ -302,19 +335,19 @@ /// public void SetLevel(int level) { - if (level == DEFAULT_COMPRESSION) + if (level == DefaultCompression) { level = 6; } - else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + else if (level < NoCompression || level > BestCompression) { - throw new ArgumentOutOfRangeException("level"); + throw new ArgumentOutOfRangeException(nameof(level)); } - if (this.level != level) + if (this.deflaterLevel != level) { - this.level = level; - engine.SetLevel(level); + this.deflaterLevel = level; + this.engine.SetLevel(level); } } @@ -324,7 +357,7 @@ /// Returns the current compression level public int GetLevel() { - return level; + return this.deflaterLevel; } /// @@ -338,7 +371,7 @@ /// public void SetStrategy(DeflateStrategy strategy) { - engine.Strategy = strategy; + this.engine.Strategy = strategy; } /// @@ -353,7 +386,7 @@ /// public int Deflate(byte[] output) { - return Deflate(output, 0, output.Length); + return this.Deflate(output, 0, output.Length); } /// @@ -382,95 +415,100 @@ { int origLength = length; - if (state == CLOSED_STATE) + if (this.state == ClosedState) { throw new InvalidOperationException("Deflater closed"); } - if (state < BUSY_STATE) + if (this.state < BusyState) { // output header - int header = (DEFLATED + - ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; - int level_flags = (level - 1) >> 1; - if (level_flags < 0 || level_flags > 3) + int header = (Deflated + + ((DeflaterConstants.MaxWbits - 8) << 4)) << 8; + int levelFlags = (this.deflaterLevel - 1) >> 1; + if (levelFlags < 0 || levelFlags > 3) { - level_flags = 3; + levelFlags = 3; } - header |= level_flags << 6; - if ((state & IS_SETDICT) != 0) + + header |= levelFlags << 6; + if ((this.state & IsSetdict) != 0) { // Dictionary was set - header |= DeflaterConstants.PRESET_DICT; + header |= DeflaterConstants.PresetDict; } + header += 31 - (header % 31); - pending.WriteShortMSB(header); - if ((state & IS_SETDICT) != 0) + this.pending.WriteShortMSB(header); + if ((this.state & IsSetdict) != 0) { - int chksum = engine.Adler; - engine.ResetAdler(); - pending.WriteShortMSB(chksum >> 16); - pending.WriteShortMSB(chksum & 0xffff); + int chksum = this.engine.Adler; + this.engine.ResetAdler(); + this.pending.WriteShortMSB(chksum >> 16); + this.pending.WriteShortMSB(chksum & 0xffff); } - state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + this.state = BusyState | (this.state & (IsFlushing | IsFinishing)); } - for (;;) + for (; ;) { - int count = pending.Flush(output, offset, length); + int count = this.pending.Flush(output, offset, length); offset += count; - totalOut += count; + this.totalOut += count; length -= count; - if (length == 0 || state == FINISHED_STATE) + if (length == 0 || this.state == FinishedState) { break; } - if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) + if (!this.engine.Deflate((this.state & IsFlushing) != 0, (this.state & IsFinishing) != 0)) { - if (state == BUSY_STATE) + if (this.state == BusyState) { // We need more input now return origLength - length; } - else if (state == FLUSHING_STATE) + else if (this.state == FlushingState) { - if (level != NO_COMPRESSION) + if (this.deflaterLevel != NoCompression) { /* 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); + int neededbits = 8 + ((-this.pending.BitCount) & 7); while (neededbits > 0) { /* write a static tree block consisting solely of * an EOF: */ - pending.WriteBits(2, 10); + this.pending.WriteBits(2, 10); neededbits -= 10; } } - state = BUSY_STATE; + + this.state = BusyState; } - else if (state == FINISHING_STATE) + else if (this.state == FinishingState) { - pending.AlignToByte(); + this.pending.AlignToByte(); // Compressed data is complete. Write footer information if required. - if (!noZlibHeaderOrFooter) + if (!this.noZlibHeaderOrFooter) { - int adler = engine.Adler; - pending.WriteShortMSB(adler >> 16); - pending.WriteShortMSB(adler & 0xffff); + int adler = this.engine.Adler; + this.pending.WriteShortMSB(adler >> 16); + this.pending.WriteShortMSB(adler & 0xffff); } - state = FINISHED_STATE; + + this.state = FinishedState; } } } + return origLength - length; } @@ -486,7 +524,7 @@ /// public void SetDictionary(byte[] dictionary) { - SetDictionary(dictionary, 0, dictionary.Length); + this.SetDictionary(dictionary, 0, dictionary.Length); } /// @@ -511,45 +549,13 @@ /// public void SetDictionary(byte[] dictionary, int index, int count) { - if (state != INIT_STATE) + if (this.state != InitState) { throw new InvalidOperationException(); } - state = SETDICT_STATE; - engine.SetDictionary(dictionary, index, count); + this.state = SetdictState; + this.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 index 1b9efbdb1..0a65ae481 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; @@ -7,139 +12,134 @@ /// 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; + public const int StoredBlock = 0; /// /// Identifies static tree in Zip file /// - public const int STATIC_TREES = 1; + public const int StaticTrees = 1; /// /// Identifies dynamic tree in Zip file /// - public const int DYN_TREES = 2; + public const int DynTrees = 2; /// /// Header flag indicating a preset dictionary for deflation /// - public const int PRESET_DICT = 0x20; + public const int PresetDict = 0x20; /// /// Sets internal buffer sizes for Huffman encoding /// - public const int DEFAULT_MEM_LEVEL = 8; + public const int DefaultMemLevel = 8; /// /// Internal compression engine constant - /// - public const int MAX_MATCH = 258; + /// + public const int MaxMatch = 258; /// /// Internal compression engine constant - /// - public const int MIN_MATCH = 3; + /// + public const int MinMatch = 3; /// /// Internal compression engine constant - /// - public const int MAX_WBITS = 15; + /// + public const int MaxWbits = 15; /// /// Internal compression engine constant - /// - public const int WSIZE = 1 << MAX_WBITS; + /// + public const int Wsize = 1 << MaxWbits; /// /// Internal compression engine constant - /// - public const int WMASK = WSIZE - 1; + /// + public const int Wmask = Wsize - 1; /// /// Internal compression engine constant - /// - public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + /// + public const int HashBits = DefaultMemLevel + 7; /// /// Internal compression engine constant - /// - public const int HASH_SIZE = 1 << HASH_BITS; + /// + public const int HashSize = 1 << HashBits; /// /// Internal compression engine constant - /// - public const int HASH_MASK = HASH_SIZE - 1; + /// + public const int HashMask = HashSize - 1; /// /// Internal compression engine constant - /// - public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + /// + public const int HashShift = (HashBits + MinMatch - 1) / MinMatch; /// /// Internal compression engine constant - /// - public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + /// + public const int MinLookahead = MaxMatch + MinMatch + 1; /// /// Internal compression engine constant - /// - public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + /// + public const int MaxDist = Wsize - MinLookahead; /// /// Internal compression engine constant - /// - public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + /// + public const int PendingBufSize = 1 << (DefaultMemLevel + 8); /// /// Internal compression engine constant - /// - public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5); + /// + public const int Deflatestored = 0; /// /// Internal compression engine constant - /// - public const int DEFLATE_STORED = 0; + /// + public const int Deflatefast = 1; /// /// Internal compression engine constant - /// - public const int DEFLATE_FAST = 1; + /// + public const int Deflateslow = 2; /// /// Internal compression engine constant - /// - public const int DEFLATE_SLOW = 2; + /// + public static int MaxBlockSize => Math.Min(65535, PendingBufSize - 5); /// /// Internal compression engine constant - /// - public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; + /// + public static int[] GoodLength => new[] { 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 }; + /// + public static int[] MaxLazy => new[] { 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 }; + /// + public static int[] NiceLength => new[] { 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 }; + /// + public static int[] MaxChain => new[] { 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 }; + /// + public static int[] ComprFunc => new[] { 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 index 15ae2ec8e..434c4727a 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; @@ -22,7 +27,7 @@ public class DeflaterEngine : DeflaterConstants { /// - /// ne more than the maximum upper bounds. + /// One more than the maximum upper bounds. /// private const int TooFar = 4096; @@ -43,11 +48,110 @@ /// private readonly short[] head; + /// + /// This array contains the part of the uncompressed stream that + /// is of relevance. The current character is indexed by strstart. + /// + private readonly byte[] window; + + /// + /// Stores the pending output of the deflator + /// + private readonly DeflaterPending pending; + + /// + /// The huffman deflator + /// + private readonly DeflaterHuffman huffman; + + /// + /// The adler checksum + /// + private readonly Adler32 adler; + /// /// Hash index of string to be inserted. /// private int insertHashIndex; + /// + /// Index of the beginning of a match. + /// + private int matchStart; + + /// + /// Length of best match + /// + private int matchLen; + + /// + /// Set if previous match exists + /// + private bool prevAvailable; + + /// + /// The index of the beinning of a block + /// + 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; + + /// + /// The maximum chain length + /// + private int maxChain; + + /// + /// The maximum lazy length + /// + private int maxLazy; + + /// + /// The nice length + /// + private int niceLength; + + /// + /// The good length + /// + 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; + /// /// Initializes a new instance of the class with a pending buffer. /// @@ -58,9 +162,9 @@ 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]; + this.window = new byte[2 * Wsize]; + this.head = new short[HashSize]; + 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. @@ -98,13 +202,13 @@ switch (this.compressionFunction) { - case DEFLATE_STORED: + case Deflatestored: progress = this.DeflateStored(canFlush, finish); break; - case DEFLATE_FAST: + case Deflatefast: progress = this.DeflateFast(canFlush, finish); break; - case DEFLATE_SLOW: + case Deflateslow: progress = this.DeflateSlow(canFlush, finish); break; default: @@ -176,15 +280,15 @@ public void SetDictionary(byte[] buffer, int offset, int length) { this.adler.Update(buffer, offset, length); - if (length < MIN_MATCH) + if (length < MinMatch) { return; } - if (length > MAX_DIST) + if (length > MaxDist) { - offset += length - MAX_DIST; - length = MAX_DIST; + offset += length - MaxDist; + length = MaxDist; } Array.Copy(buffer, offset, this.window, this.strstart, length); @@ -212,14 +316,14 @@ this.lookahead = 0; this.totalIn = 0; this.prevAvailable = false; - this.matchLen = MIN_MATCH - 1; + this.matchLen = MinMatch - 1; - for (int i = 0; i < HASH_SIZE; i++) + for (int i = 0; i < HashSize; i++) { this.head[i] = 0; } - for (int i = 0; i < WSIZE; i++) + for (int i = 0; i < Wsize; i++) { this.previousIndex[i] = 0; } @@ -244,16 +348,16 @@ 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]; + this.goodLength = GoodLength[level]; + this.maxLazy = MaxLazy[level]; + this.niceLength = NiceLength[level]; + this.maxChain = MaxChain[level]; - if (COMPR_FUNC[level] != this.compressionFunction) + if (ComprFunc[level] != this.compressionFunction) { switch (this.compressionFunction) { - case DEFLATE_STORED: + case Deflatestored: if (this.strstart > this.blockStart) { this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); @@ -263,17 +367,16 @@ this.UpdateHash(); break; - case DEFLATE_FAST: + case Deflatefast: if (this.strstart > this.blockStart) { - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, - false); + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); this.blockStart = this.strstart; } break; - case DEFLATE_SLOW: + case Deflateslow: if (this.prevAvailable) { this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff); @@ -286,31 +389,31 @@ } this.prevAvailable = false; - this.matchLen = MIN_MATCH - 1; + this.matchLen = MinMatch - 1; break; } - this.compressionFunction = COMPR_FUNC[level]; + this.compressionFunction = ComprFunc[level]; } } /// - /// Fill the window + /// Fills 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) + if (this.strstart >= Wsize + MaxDist) { 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) + while (this.lookahead < MinLookahead && this.inputOff < this.inputEnd) { - int more = (2 * WSIZE) - this.lookahead - this.strstart; + int more = (2 * Wsize) - this.lookahead - this.strstart; if (more > this.inputEnd - this.inputOff) { @@ -325,15 +428,18 @@ this.lookahead += more; } - if (this.lookahead >= MIN_MATCH) + if (this.lookahead >= MinMatch) { this.UpdateHash(); } } + /// + /// Updates this hash. + /// private void UpdateHash() { - this.insertHashIndex = (this.window[this.strstart] << HASH_SHIFT) ^ this.window[this.strstart + 1]; + this.insertHashIndex = (this.window[this.strstart] << HashShift) ^ this.window[this.strstart + 1]; } /// @@ -344,34 +450,37 @@ private int InsertString() { short match; - int hash = ((this.insertHashIndex << HASH_SHIFT) ^ this.window[this.strstart + (MIN_MATCH - 1)]) & HASH_MASK; + int hash = ((this.insertHashIndex << HashShift) ^ this.window[this.strstart + (MinMatch - 1)]) & HashMask; - this.previousIndex[this.strstart & WMASK] = match = this.head[hash]; + this.previousIndex[this.strstart & Wmask] = match = this.head[hash]; this.head[hash] = unchecked((short)this.strstart); this.insertHashIndex = hash; return match & 0xffff; } + /// + /// Slides the current byte window to the ewlefvent part of the uncompressed stream. + /// private void SlideWindow() { - Array.Copy(this.window, WSIZE, this.window, 0, WSIZE); - this.matchStart -= WSIZE; - this.strstart -= WSIZE; - this.blockStart -= WSIZE; + 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) + for (int i = 0; i < HashSize; ++i) { int m = this.head[i] & 0xffff; - this.head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); + this.head[i] = (short)(m >= Wsize ? (m - Wsize) : 0); } // Slide the prev table. - for (int i = 0; i < WSIZE; i++) + for (int i = 0; i < Wsize; i++) { int m = this.previousIndex[i] & 0xffff; - this.previousIndex[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); + this.previousIndex[i] = (short)(m >= Wsize ? (m - Wsize) : 0); } } @@ -392,11 +501,11 @@ short[] previous = this.previousIndex; int scan = this.strstart; int bestEnd = this.strstart + this.matchLen; - int bestLength = Math.Max(this.matchLen, MIN_MATCH - 1); + int bestLength = Math.Max(this.matchLen, MinMatch - 1); - int limit = Math.Max(this.strstart - MAX_DIST, 0); + int limit = Math.Max(this.strstart - MaxDist, 0); - int strend = this.strstart + MAX_MATCH - 1; + int strend = this.strstart + MaxMatch - 1; byte scanEnd1 = this.window[bestEnd - 1]; byte scanEnd = this.window[bestEnd]; @@ -458,12 +567,19 @@ } scan = this.strstart; - } while ((curMatch = previous[curMatch & WMASK] & 0xffff) > limit && --chainLength != 0); + } + while ((curMatch = previous[curMatch & Wmask] & 0xffff) > limit && --chainLength != 0); this.matchLen = Math.Min(bestLength, this.lookahead); - return this.matchLen >= MIN_MATCH; + return this.matchLen >= MinMatch; } + /// + /// Returns a value indicating whether the uncompressed block is stored. + /// + /// Whether to flush the stream. + /// Whether to finish the stream. + /// The private bool DeflateStored(bool flush, bool finish) { if (!flush && (this.lookahead == 0)) @@ -476,14 +592,14 @@ 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 + if ((storedLength >= MaxBlockSize) || // Block is full + (this.blockStart < Wsize && storedLength >= MaxDist) || // Block may move out of window flush) { bool lastBlock = finish; - if (storedLength > MAX_BLOCK_SIZE) + if (storedLength > MaxBlockSize) { - storedLength = MAX_BLOCK_SIZE; + storedLength = MaxBlockSize; lastBlock = false; } @@ -495,14 +611,20 @@ return true; } + /// + /// Performs a fast deflation of the input stream return a value to indicate succes. + /// + /// Whether to flush the stream. + /// Whether to finish the stream. + /// The private bool DeflateFast(bool flush, bool finish) { - if (this.lookahead < MIN_LOOKAHEAD && !flush) + if (this.lookahead < MinLookahead && !flush) { return false; } - while (this.lookahead >= MIN_LOOKAHEAD || flush) + while (this.lookahead >= MinLookahead || flush) { if (this.lookahead == 0) { @@ -512,27 +634,26 @@ return false; } - if (this.strstart > (2 * WSIZE) - MIN_LOOKAHEAD) + if (this.strstart > (2 * Wsize) - MinLookahead) { - /* slide window, as FindLongestMatch needs this. - * This should only happen when flushing and the window - * is almost full. - */ + // 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 && + if (this.lookahead >= MinMatch && (hashHead = this.InsertString()) != 0 && this.Strategy != DeflateStrategy.HuffmanOnly && - this.strstart - hashHead <= MAX_DIST && + this.strstart - hashHead <= MaxDist && 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) + if (this.matchLen <= this.maxLazy && this.lookahead >= MinMatch) { while (--this.matchLen > 0) { @@ -545,13 +666,13 @@ else { this.strstart += this.matchLen; - if (this.lookahead >= MIN_MATCH - 1) + if (this.lookahead >= MinMatch - 1) { this.UpdateHash(); } } - this.matchLen = MIN_MATCH - 1; + this.matchLen = MinMatch - 1; if (!full) { continue; @@ -577,14 +698,20 @@ return true; } + /// + /// Performs a slow deflation of the input stream return a value to indicate succes. + /// + /// Whether to flush the stream. + /// Whether to finish the stream. + /// The private bool DeflateSlow(bool flush, bool finish) { - if (this.lookahead < MIN_LOOKAHEAD && !flush) + if (this.lookahead < MinLookahead && !flush) { return false; } - while (this.lookahead >= MIN_LOOKAHEAD || flush) + while (this.lookahead >= MinLookahead || flush) { if (this.lookahead == 0) { @@ -596,13 +723,12 @@ this.prevAvailable = false; // We are flushing everything - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, - finish); + 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) + if (this.strstart >= (2 * Wsize) - MinLookahead) { // slide window, as FindLongestMatch needs this. // This should only happen when flushing and the window @@ -612,29 +738,26 @@ int prevMatch = this.matchStart; int prevLen = this.matchLen; - if (this.lookahead >= MIN_MATCH) + if (this.lookahead >= MinMatch) { - int hashHead = this.InsertString(); if (this.Strategy != DeflateStrategy.HuffmanOnly && hashHead != 0 && - this.strstart - hashHead <= MAX_DIST && + this.strstart - hashHead <= MaxDist && 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))) + if (this.matchLen <= 5 && (this.Strategy == DeflateStrategy.Filtered || (this.matchLen == MinMatch && this.strstart - this.matchStart > TooFar))) { - this.matchLen = MIN_MATCH - 1; + this.matchLen = MinMatch - 1; } } } // previous match was better - if ((prevLen >= MIN_MATCH) && (this.matchLen <= prevLen)) + if ((prevLen >= MinMatch) && (this.matchLen <= prevLen)) { this.huffman.TallyDist(this.strstart - 1 - prevMatch, prevLen); prevLen -= 2; @@ -642,16 +765,17 @@ { this.strstart++; this.lookahead--; - if (this.lookahead >= MIN_MATCH) + if (this.lookahead >= MinMatch) { this.InsertString(); } - } while (--prevLen > 0); + } + while (--prevLen > 0); this.strstart++; this.lookahead--; this.prevAvailable = false; - this.matchLen = MIN_MATCH - 1; + this.matchLen = MinMatch - 1; } else { @@ -682,76 +806,5 @@ 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 index 8ea1369c1..5d6857d41 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs @@ -12,125 +12,135 @@ /// public class DeflaterHuffman { - const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); - const int LITERAL_NUM = 286; + /// + /// The buffer size. + /// + private const int Buffersize = 1 << (DeflaterConstants.DefaultMemLevel + 6); - // Number of distance codes - const int DIST_NUM = 30; - // Number of codes used to transfer bit lengths - const int BITLEN_NUM = 19; + /// + /// The number of literals. + /// + private const int LiteralCount = 286; - // 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; + /// + /// Number of distance codes + /// + private const int DistanceCodeCount = 30; - const int EOF_SYMBOL = 256; + /// + /// Number of codes used to transfer bit lengths + /// + private const int BitLengthCount = 19; - // 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 }; + /// + /// Repeat previous bit length 3-6 times (2 bits of repeat count) + /// + private const int Repeat3To6 = 16; - 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; + /// + /// Repeat a zero length 3-10 times (3 bits of repeat count) + /// + private const int Repeat3To10 = 17; - public byte[] length; + /// + /// Repeat a zero length 11-138 times (7 bits of repeat count) + /// + private const int Repeat11To138 = 18; - public int minNumCodes; + /// + /// The end of file flag. + /// + private const int Eof = 256; - public int numCodes; + /// + /// The lengths of the bit length codes are sent in order of decreasing + /// probability, to avoid transmitting the lengths for unused bit length codes. + /// + private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - short[] codes; - int[] bl_counts; - int maxLength; - DeflaterHuffman dh; - #endregion + /// + /// Bit data reversed. + /// + private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + + /// + /// The literal codes. + /// + private static short[] staticLCodes; + private static byte[] staticLLength; + private static short[] staticDCodes; + private static byte[] staticDLength; - #region Constructors - public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) + /// + /// A binary tree, with the property, that the parent node is smaller than both child nodes. + /// + private class Tree + { + /// + /// The minimum number of codes. + /// + private readonly int minimumNumberOfCodes; + + /// + /// The array of codes. + /// + private short[] codes; + private readonly int[] blCounts; + private readonly int maxLength; + private readonly DeflaterHuffman deflater; + + /// + /// Initializes a new instance of the class. + /// + /// The + /// The elements. + /// The minimum number of codes. + /// The maximum length. + public Tree(DeflaterHuffman huffman, int elems, int minCodes, int maxLength) { - this.dh = dh; - this.minNumCodes = minCodes; + this.deflater = huffman; + this.minimumNumberOfCodes = minCodes; this.maxLength = maxLength; - freqs = new short[elems]; - bl_counts = new int[maxLength]; + this.Frequencies = new short[elems]; + this.blCounts = new int[maxLength]; } - #endregion + /// + /// Gets the number of codes. + /// + public int NumberOfCodes { get; private set; } + + /// + /// Gets the frequencies. + /// + public short[] Frequencies { get; } + + /// + /// Gets or sets the length. + /// + public byte[] Length { get; private set; } /// /// Resets the internal state of the tree /// public void Reset() { - for (int i = 0; i < freqs.Length; i++) + for (int i = 0; i < this.Frequencies.Length; i++) { - freqs[i] = 0; + this.Frequencies[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]); + this.codes = null; + this.Length = null; } /// - /// Check that all frequencies are zero + /// Writes a code symbol. /// - /// - /// At least one frequency is non-zero - /// - public void CheckEmpty() + /// The code index. + public void WriteSymbol(int code) { - 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"); - } + this.deflater.pending.WriteBits(this.codes[code] & 0xffff, this.Length[code]); } /// @@ -140,8 +150,8 @@ /// length for new codes public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) { - codes = staticCodes; - length = staticLengths; + this.codes = staticCodes; + this.Length = staticLengths; } /// @@ -149,45 +159,24 @@ /// public void BuildCodes() { - int numSymbols = freqs.Length; - int[] nextCode = new int[maxLength]; + int numSymbols = this.Frequencies.Length; + int[] nextCode = new int[this.maxLength]; int code = 0; - codes = new short[freqs.Length]; + this.codes = new short[numSymbols]; - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("buildCodes: "+freqs.Length); - // } - - for (int bits = 0; bits < maxLength; bits++) + for (int bits = 0; bits < this.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); - // } + code += this.blCounts[bits] << (15 - bits); } -#if DebugDeflation - if ( DeflaterConstants.DEBUGGING && (code != 65536) ) - { - throw new ImageFormatException("Inconsistent bl_counts!"); - } -#endif - for (int i = 0; i < numCodes; i++) + for (int i = 0; i < this.NumberOfCodes; i++) { - int bits = length[i]; + int bits = this.Length[i]; if (bits > 0) { - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+"), - // +bits); - // } - - codes[i] = BitReverse(nextCode[bits - 1]); + this.codes[i] = BitReverse(nextCode[bits - 1]); nextCode[bits - 1] += 1 << (16 - bits); } } @@ -195,67 +184,65 @@ 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 numSymbols = this.Frequencies.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]; + int freq = this.Frequencies[n]; if (freq != 0) { // Insert n into heap int pos = heapLen++; int ppos; - while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) + while (pos > 0 && this.Frequencies[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. - */ + // 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); + this.NumberOfCodes = Math.Max(maxCode + 1, this.minimumNumberOfCodes); int numLeafs = heapLen; - int[] childs = new int[4 * heapLen - 2]; - int[] values = new int[2 * heapLen - 1]; + 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; + childs[(2 * i) + 1] = -1; + values[i] = this.Frequencies[node] << 8; heap[i] = i; } - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. do { int first = heap[0]; @@ -274,26 +261,25 @@ heap[ppos] = heap[path]; ppos = path; - path = path * 2 + 1; + path = (path * 2) + 1; } - /* Now propagate the last element down along path. Normally - * it shouldn't go too deep. - */ + // 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; + 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; + childs[(2 * last) + 1] = second; int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); values[last] = lastVal = values[first] + values[second] - mindepth + 1; @@ -310,7 +296,7 @@ heap[ppos] = heap[path]; ppos = path; - path = ppos * 2 + 1; + path = (ppos * 2) + 1; } // Now propagate the new element down along path @@ -318,15 +304,17 @@ { heap[path] = heap[ppos]; } + heap[path] = last; - } while (heapLen > 1); + } + while (heapLen > 1); if (heap[0] != (childs.Length / 2) - 1) { throw new ImageFormatException("Heap invariant violated"); } - BuildLength(childs); + this.BuildLength(childs); } /// @@ -336,10 +324,11 @@ public int GetEncodedLength() { int len = 0; - for (int i = 0; i < freqs.Length; i++) + for (int i = 0; i < this.Frequencies.Length; i++) { - len += freqs[i] * length[i]; + len += this.Frequencies[i] * this.Length[i]; } + return len; } @@ -355,10 +344,10 @@ int curlen = -1; /* length of current code */ int i = 0; - while (i < numCodes) + while (i < this.NumberOfCodes) { count = 1; - int nextlen = length[i]; + int nextlen = this.Length[i]; if (nextlen == 0) { max_count = 138; @@ -370,14 +359,15 @@ min_count = 3; if (curlen != nextlen) { - blTree.freqs[nextlen]++; + blTree.Frequencies[nextlen]++; count = 0; } } + curlen = nextlen; i++; - while (i < numCodes && curlen == length[i]) + while (i < this.NumberOfCodes && curlen == this.Length[i]) { i++; if (++count >= max_count) @@ -388,19 +378,19 @@ if (count < min_count) { - blTree.freqs[curlen] += (short)count; + blTree.Frequencies[curlen] += (short)count; } else if (curlen != 0) { - blTree.freqs[REP_3_6]++; + blTree.Frequencies[Repeat3To6]++; } else if (count <= 10) { - blTree.freqs[REP_3_10]++; + blTree.Frequencies[Repeat3To10]++; } else { - blTree.freqs[REP_11_138]++; + blTree.Frequencies[Repeat11To138]++; } } } @@ -417,10 +407,10 @@ int curlen = -1; // length of current code int i = 0; - while (i < numCodes) + while (i < this.NumberOfCodes) { count = 1; - int nextlen = length[i]; + int nextlen = this.Length[i]; if (nextlen == 0) { max_count = 138; @@ -436,10 +426,11 @@ count = 0; } } + curlen = nextlen; i++; - while (i < numCodes && curlen == length[i]) + while (i < this.NumberOfCodes && curlen == this.Length[i]) { i++; if (++count >= max_count) @@ -457,32 +448,32 @@ } else if (curlen != 0) { - blTree.WriteSymbol(REP_3_6); - dh.pending.WriteBits(count - 3, 2); + blTree.WriteSymbol(Repeat3To6); + this.deflater.pending.WriteBits(count - 3, 2); } else if (count <= 10) { - blTree.WriteSymbol(REP_3_10); - dh.pending.WriteBits(count - 3, 3); + blTree.WriteSymbol(Repeat3To10); + this.deflater.pending.WriteBits(count - 3, 3); } else { - blTree.WriteSymbol(REP_11_138); - dh.pending.WriteBits(count - 11, 7); + blTree.WriteSymbol(Repeat11To138); + this.deflater.pending.WriteBits(count - 11, 7); } } } - void BuildLength(int[] childs) + private void BuildLength(int[] childs) { - this.length = new byte[freqs.Length]; + this.Length = new byte[this.Frequencies.Length]; int numNodes = childs.Length / 2; int numLeafs = (numNodes + 1) / 2; int overflow = 0; - for (int i = 0; i < maxLength; i++) + for (int i = 0; i < this.maxLength; i++) { - bl_counts[i] = 0; + this.blCounts[i] = 0; } // First calculate optimal bit lengths @@ -491,96 +482,80 @@ for (int i = numNodes - 1; i >= 0; i--) { - if (childs[2 * i + 1] != -1) + if (childs[(2 * i) + 1] != -1) { int bitLength = lengths[i] + 1; - if (bitLength > maxLength) + if (bitLength > this.maxLength) { - bitLength = maxLength; + bitLength = this.maxLength; overflow++; } - lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; + + 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]; + this.blCounts[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; + int incrBitLen = this.maxLength - 1; do { // Find the first bit length which could increase: - while (bl_counts[--incrBitLen] == 0) - ; + while (this.blCounts[--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. - */ + this.blCounts[incrBitLen]--; + this.blCounts[++incrBitLen]++; + overflow -= 1 << (this.maxLength - 1 - incrBitLen); + } + while (overflow > 0 && incrBitLen < this.maxLength - 1); + } + while (overflow > 0); + + // We may have overshot above. Move some nodes from maxLength to + // maxLength-1 in that case. + this.blCounts[this.maxLength - 1] += overflow; + this.blCounts[this.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--) + for (int bits = this.maxLength; bits != 0; bits--) { - int n = bl_counts[bits - 1]; + int n = this.blCounts[bits - 1]; while (n > 0) { int childPtr = 2 * childs[nodePtr++]; if (childs[childPtr + 1] == -1) { // We found another leaf - length[childs[childPtr]] = (byte)bits; + this.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 /// @@ -595,14 +570,13 @@ 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]; + staticLCodes = new short[LiteralCount]; + staticLLength = new byte[LiteralCount]; int i = 0; while (i < 144) @@ -623,16 +597,16 @@ staticLLength[i++] = 7; } - while (i < LITERAL_NUM) + while (i < LiteralCount) { 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 = new short[DistanceCodeCount]; + staticDLength = new byte[DistanceCodeCount]; + for (i = 0; i < DistanceCodeCount; i++) { staticDCodes[i] = BitReverse(i << 11); staticDLength[i] = 5; @@ -640,31 +614,44 @@ } /// - /// Construct instance with pending buffer + /// Initializes a new instance of the class with a 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); + this.literalTree = new Tree(this, LiteralCount, 257, 15); + this.distTree = new Tree(this, DistanceCodeCount, 1, 15); + this.blTree = new Tree(this, BitLengthCount, 4, 7); - d_buf = new short[BUFSIZE]; - l_buf = new byte[BUFSIZE]; + this.d_buf = new short[Buffersize]; + this.l_buf = new byte[Buffersize]; } /// - /// Reset internal state + /// 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]); + } + + /// + /// Resets the internal state /// public void Reset() { - last_lit = 0; - extra_bits = 0; - literalTree.Reset(); - distTree.Reset(); - blTree.Reset(); + this.last_lit = 0; + this.extra_bits = 0; + this.literalTree.Reset(); + this.distTree.Reset(); + this.blTree.Reset(); } /// @@ -673,24 +660,20 @@ /// 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); + this.blTree.BuildCodes(); + this.literalTree.BuildCodes(); + this.distTree.BuildCodes(); + this.pending.WriteBits(this.literalTree.NumberOfCodes - 257, 5); + this.pending.WriteBits(this.distTree.NumberOfCodes - 1, 5); + this.pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) { - pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); + this.pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3); } - literalTree.WriteTree(blTree); - distTree.WriteTree(blTree); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - blTree.CheckEmpty(); - } -#endif + + this.literalTree.WriteTree(this.blTree); + this.distTree.WriteTree(this.blTree); } /// @@ -698,60 +681,37 @@ /// public void CompressBlock() { - for (int i = 0; i < last_lit; i++) + for (int i = 0; i < this.last_lit; i++) { - int litlen = l_buf[i] & 0xff; - int dist = d_buf[i]; + int litlen = this.l_buf[i] & 0xff; + int dist = this.d_buf[i]; if (dist-- != 0) { - // if (DeflaterConstants.DEBUGGING) { - // Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); - // } - int lc = Lcode(litlen); - literalTree.WriteSymbol(lc); + this.literalTree.WriteSymbol(lc); int bits = (lc - 261) / 4; if (bits > 0 && bits <= 5) { - pending.WriteBits(litlen & ((1 << bits) - 1), bits); + this.pending.WriteBits(litlen & ((1 << bits) - 1), bits); } int dc = Dcode(dist); - distTree.WriteSymbol(dc); + this.distTree.WriteSymbol(dc); - bits = dc / 2 - 1; + bits = (dc / 2) - 1; if (bits > 0) { - pending.WriteBits(dist & ((1 << bits) - 1), bits); + this.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); + this.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 + this.literalTree.WriteSymbol(Eof); } /// @@ -763,17 +723,12 @@ /// 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(); + this.pending.WriteBits((DeflaterConstants.StoredBlock << 1) + (lastBlock ? 1 : 0), 3); + this.pending.AlignToByte(); + this.pending.WriteShort(storedLength); + this.pending.WriteShort(~storedLength); + this.pending.WriteBlock(stored, storedOffset, storedLength); + this.Reset(); } /// @@ -785,40 +740,43 @@ /// True if this is the last block public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) { - literalTree.freqs[EOF_SYMBOL]++; + this.literalTree.Frequencies[Eof]++; // Build trees - literalTree.BuildTree(); - distTree.BuildTree(); + this.literalTree.BuildTree(); + this.distTree.BuildTree(); // Calculate bitlen frequency - literalTree.CalcBLFreq(blTree); - distTree.CalcBLFreq(blTree); + this.literalTree.CalcBLFreq(this.blTree); + this.distTree.CalcBLFreq(this.blTree); // Build bitlen tree - blTree.BuildTree(); + this.blTree.BuildTree(); int blTreeCodes = 4; for (int i = 18; i > blTreeCodes; i--) { - if (blTree.length[BL_ORDER[i]] > 0) + if (this.blTree.Length[BitLengthOrder[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++) + int opt_len = 14 + (blTreeCodes * 3) + this.blTree.GetEncodedLength() + + this.literalTree.GetEncodedLength() + this.distTree.GetEncodedLength() + + this.extra_bits; + + int static_len = this.extra_bits; + for (int i = 0; i < LiteralCount; i++) { - static_len += literalTree.freqs[i] * staticLLength[i]; + static_len += this.literalTree.Frequencies[i] * staticLLength[i]; } - for (int i = 0; i < DIST_NUM; i++) + + for (int i = 0; i < DistanceCodeCount; i++) { - static_len += distTree.freqs[i] * staticDLength[i]; + static_len += this.distTree.Frequencies[i] * staticDLength[i]; } + if (opt_len >= static_len) { // Force static trees @@ -828,29 +786,24 @@ 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); + this.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(); + this.pending.WriteBits((DeflaterConstants.StaticTrees << 1) + (lastBlock ? 1 : 0), 3); + this.literalTree.SetStaticCodes(staticLCodes, staticLLength); + this.distTree.SetStaticCodes(staticDCodes, staticDLength); + this.CompressBlock(); + this.Reset(); } else { // Encode with dynamic tree - pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); - SendAllTrees(blTreeCodes); - CompressBlock(); - Reset(); + this.pending.WriteBits((DeflaterConstants.DynTrees << 1) + (lastBlock ? 1 : 0), 3); + this.SendAllTrees(blTreeCodes); + this.CompressBlock(); + this.Reset(); } } @@ -860,7 +813,7 @@ /// true if buffer is full public bool IsFull() { - return last_lit >= BUFSIZE; + return this.last_lit >= Buffersize; } /// @@ -870,17 +823,10 @@ /// 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(); + this.d_buf[this.last_lit] = 0; + this.l_buf[this.last_lit++] = (byte)literal; + this.literalTree.Frequencies[literal]++; + return this.IsFull(); } /// @@ -891,44 +837,27 @@ /// 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); + this.d_buf[this.last_lit] = (short)distance; + this.l_buf[this.last_lit++] = (byte)(length - 3); int lc = Lcode(length - 3); - literalTree.freqs[lc]++; + this.literalTree.Frequencies[lc]++; if (lc >= 265 && lc < 285) { - extra_bits += (lc - 261) / 4; + this.extra_bits += (lc - 261) / 4; } int dc = Dcode(distance - 1); - distTree.freqs[dc]++; + this.distTree.Frequencies[dc]++; if (dc >= 4) { - extra_bits += dc / 2 - 1; + this.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]); + return this.IsFull(); } - static int Lcode(int length) + private static int Lcode(int length) { if (length == 255) { @@ -941,10 +870,11 @@ code += 4; length >>= 1; } + return code + length; } - static int Dcode(int distance) + private static int Dcode(int distance) { int code = 0; while (distance >= 4) @@ -952,6 +882,7 @@ 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 index 2f4d9eefd..aa05aed70 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; using System.IO; @@ -6,13 +11,49 @@ /// /// 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 + /// 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 deflater which is used to deflate the stream. + /// + private readonly Deflater deflater; + + /// + /// Base stream the deflater depends on. + /// + private readonly Stream baseOutputStream; + + /// + /// This buffer is used temporarily to retrieve the bytes from the + /// deflater and write them to the underlying output stream. + /// + private readonly byte[] bytebuffer; + + /// + /// The password + /// + private string password; + + /// + /// The keys + /// + private uint[] keys; + + /// + /// Whether the stream is closed + /// + private bool isClosed; + + /// + /// Whether dispose should close the underlying stream. + /// + private bool isStreamOwner = true; + + /// + /// Initializes a new instance of the class + /// with a default Deflater and default buffer size. /// /// /// the output stream where deflated output should be written. @@ -23,8 +64,8 @@ } /// - /// Creates a new DeflaterOutputStream with the given Deflater and - /// default buffer size. + /// Initializes a new instance of the class + /// with the given Deflater and default buffer size. /// /// /// the output stream where deflated output should be written. @@ -38,8 +79,8 @@ } /// - /// Creates a new DeflaterOutputStream with the given Deflater and - /// buffer size. + /// Initializes a new instance of the class + /// with the given Deflater and buffer size. /// /// /// The output stream where deflated output is written. @@ -50,80 +91,34 @@ /// /// 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 - /// + /// buffersize 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"); + throw new ArgumentNullException(nameof(baseOutputStream)); } if (baseOutputStream.CanWrite == false) { - throw new ArgumentException("Must support writing", "baseOutputStream"); + throw new ArgumentException("Must support writing", nameof(baseOutputStream)); } if (deflater == null) { - throw new ArgumentNullException("deflater"); + throw new ArgumentNullException(nameof(deflater)); } if (bufferSize < 512) { - throw new ArgumentOutOfRangeException("bufferSize"); + throw new ArgumentOutOfRangeException(nameof(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; - } + this.baseOutputStream = baseOutputStream; + this.bytebuffer = new byte[bufferSize]; + this.deflater = deflater; } /// @@ -132,28 +127,14 @@ /// public bool IsStreamOwner { - get { return isStreamOwner_; } - set { isStreamOwner_ = value; } + get { return this.isStreamOwner; } + set { this.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; + public bool CanPatchEntries => this.baseOutputStream.CanSeek; /// /// Get/set the password used for encryption. @@ -163,189 +144,42 @@ { get { - return password; + return this.password; } + set { if ((value != null) && (value.Length == 0)) { - password = null; + this.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); + this.password = value; } - - 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; - } - } + public override bool CanRead => false; /// /// Gets a value indicating if seeking is supported for this stream /// This property always returns false /// - public override bool CanSeek - { - get - { - return false; - } - } + public override bool CanSeek => false; /// /// Get value indicating if this stream supports writing /// - public override bool CanWrite - { - get - { - return baseOutputStream_.CanWrite; - } - } + public override bool CanWrite => this.baseOutputStream.CanWrite; /// /// Get current length of stream /// - public override long Length - { - get - { - return baseOutputStream_.Length; - } - } + public override long Length => this.baseOutputStream.Length; /// /// Gets the current position within the stream. @@ -355,14 +189,49 @@ { get { - return baseOutputStream_.Position; + return this.baseOutputStream.Position; } + set { throw new NotSupportedException("Position property not supported"); } } + /// + /// Finishes the stream by calling finish() on the deflater. + /// + /// + /// Not all input is deflated + /// + public virtual void Finish() + { + this.deflater.Finish(); + while (!this.deflater.IsFinished) + { + int len = this.deflater.Deflate(this.bytebuffer, 0, this.bytebuffer.Length); + if (len <= 0) + { + break; + } + + if (this.keys != null) + { + this.EncryptBlock(this.bytebuffer, 0, len); + } + + this.baseOutputStream.Write(this.bytebuffer, 0, len); + } + + if (!this.deflater.IsFinished) + { + throw new ImageFormatException("Can't deflate all input?"); + } + + this.baseOutputStream.Flush(); + this.keys = null; + } + /// /// Sets the current position of this stream to the given value. Not supported by this class! /// @@ -407,109 +276,16 @@ { 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 + this.deflater.Flush(); + this.Deflate(); + this.baseOutputStream.Flush(); } /// @@ -522,7 +298,7 @@ { byte[] b = new byte[1]; b[0] = value; - Write(b, 0, 1); + this.Write(b, 0, 1); } /// @@ -539,39 +315,132 @@ /// public override void Write(byte[] buffer, int offset, int count) { - deflater_.SetInput(buffer, offset, count); - Deflate(); + this.deflater.SetInput(buffer, offset, count); + this.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. + /// 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] ^= this.EncryptByte(); + this.UpdateKeys(oldbyte); + } + } + + /// + /// Initializes encryption keys based on given . /// - byte[] buffer_; + /// The password. + protected void InitializePassword(string pssword) + { + this.keys = new uint[] + { + 0x12345678, + 0x23456789, + 0x34567890 + }; + + byte[] rawPassword = ZipConstants.ConvertToArray(pssword); + + foreach (byte b in rawPassword) + { + this.UpdateKeys(b); + } + } /// - /// The deflater which is used to deflate the stream. + /// Encrypt a single byte /// - protected Deflater deflater_; + /// + /// The encrypted value + /// + protected byte EncryptByte() + { + uint temp = (this.keys[2] & 0xFFFF) | 2; + return (byte)((temp * (temp ^ 1)) >> 8); + } /// - /// Base stream the deflater depends on. + /// Update encryption keys /// - protected Stream baseOutputStream_; + /// The character. + protected void UpdateKeys(byte ch) + { + this.keys[0] = Crc32.ComputeCrc32(this.keys[0], ch); + this.keys[1] = this.keys[1] + (byte)this.keys[0]; + this.keys[1] = (this.keys[1] * 134775813) + 1; + this.keys[2] = Crc32.ComputeCrc32(this.keys[2], (byte)(this.keys[1] >> 24)); + } - bool isClosed_; + /// + /// Deflates everything in the input buffers. This will call + /// def.deflate() until all bytes from the input buffers + /// are processed. + /// + protected void Deflate() + { + while (!this.deflater.IsNeedingInput) + { + int deflateCount = this.deflater.Deflate(this.bytebuffer, 0, this.bytebuffer.Length); - bool isStreamOwner_ = true; - #endregion + if (deflateCount <= 0) + { + break; + } + + if (this.keys != null) + { + this.EncryptBlock(this.bytebuffer, 0, deflateCount); + } - #region Static Fields + this.baseOutputStream.Write(this.bytebuffer, 0, deflateCount); + } + + if (!this.deflater.IsNeedingInput) + { + throw new ImageFormatException("DeflaterOutputStream can't deflate all input?"); + } + } + + /// + /// Calls and closes the underlying + /// stream when is true. + /// + /// If true, the object gets disposed. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing && !this.isClosed) + { + this.isClosed = true; -#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 + try + { + this.Finish(); + this.keys = null; + } + finally + { + if (this.isStreamOwner) + { + this.baseOutputStream.Dispose(); + } + } + } + } } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs index 70df58382..79679a092 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs @@ -1,8 +1,13 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { /// /// This class stores the pending output of the Deflater. - /// + /// /// author of the original java version : Jochen Hoenicke /// public class DeflaterPending : PendingBuffer @@ -12,7 +17,7 @@ /// Construct instance with default buffer size /// public DeflaterPending() - : base(DeflaterConstants.PENDING_BUF_SIZE) + : base(DeflaterConstants.PendingBufSize) { } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs b/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs index 1325b8662..9cf75c920 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs @@ -1,4 +1,9 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; diff --git a/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs index c23a15311..35a96741d 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs @@ -1,10 +1,11 @@ -namespace ImageProcessor.Formats { +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 @@ -28,19 +29,20 @@ /// 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 - }; + private 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 = { + private 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 }; @@ -48,7 +50,8 @@ /// /// Copy offsets for distance codes 0..29 /// - static readonly int[] CPDIST = { + private 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 @@ -57,7 +60,8 @@ /// /// Extra bits for distance codes /// - static readonly int[] CPDEXT = { + private 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 @@ -66,95 +70,92 @@ /// /// 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 + private const int DECODE_HEADER = 0; + private const int DECODE_DICT = 1; + private const int DECODE_BLOCKS = 2; + private const int DECODE_STORED_LEN1 = 3; + private const int DECODE_STORED_LEN2 = 4; + private const int DECODE_STORED = 5; + private const int DECODE_DYN_HEADER = 6; + private const int DECODE_HUFFMAN = 7; + private const int DECODE_HUFFMAN_LENBITS = 8; + private const int DECODE_HUFFMAN_DIST = 9; + private const int DECODE_HUFFMAN_DISTBITS = 10; + private const int DECODE_CHKSUM = 11; + private const int FINISHED = 12; + /// /// This variable contains the current state. /// - int mode; + private int mode; /// /// The adler checksum of the dictionary or of the decompressed /// stream, as it is written in the header resp. footer of the - /// compressed stream. + /// compressed stream. /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. /// - int readAdler; + private int readAdler; /// /// The number of bits needed to complete the current state. This /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. /// - int neededBits; - int repLength; - int repDist; - int uncomprLen; + private int neededBits; + private int repLength; + private int repDist; + private int uncomprLen; /// /// True, if the last block flag was set in the last block of the /// inflated stream. This means that the stream ends after the /// current block. /// - bool isLastBlock; + private bool isLastBlock; /// /// The total number of inflated bytes. /// - long totalOut; + private 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; + private 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 + /// True means, that the inflated stream doesn't contain a Zlib header or /// footer. /// - bool noHeader; + private bool noHeader; - StreamManipulator input; - OutputWindow outputWindow; - InflaterDynHeader dynHeader; - InflaterHuffmanTree litlenTree, distTree; - Adler32 adler; - #endregion + private StreamManipulator input; + private OutputWindow outputWindow; + private InflaterDynHeader dynHeader; + private InflaterHuffmanTree litlenTree, distTree; + private Adler32 adler; - #region Constructors /// - /// Creates a new inflater or RFC1951 decompressor + /// Initializes a new instance of the class. /// RFC1950/Zlib headers and footers will be expected in the input data /// - public Inflater() : this(false) + public Inflater() + : this(false) { } /// - /// Creates a new inflater. + /// Initializes a new instance of the class. /// /// /// 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. @@ -163,11 +164,10 @@ { this.noHeader = noHeader; this.adler = new Adler32(); - input = new StreamManipulator(); - outputWindow = new OutputWindow(); - mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; + this.input = new StreamManipulator(); + this.outputWindow = new OutputWindow(); + this.mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; } - #endregion /// /// Resets the inflater so that a new stream can be decompressed. All @@ -175,16 +175,16 @@ /// 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(); + this.mode = this.noHeader ? DECODE_BLOCKS : DECODE_HEADER; + this.totalIn = 0; + this.totalOut = 0; + this.input.Reset(); + this.outputWindow.Reset(); + this.dynHeader = null; + this.litlenTree = null; + this.distTree = null; + this.isLastBlock = false; + this.adler.Reset(); } /// @@ -198,12 +198,13 @@ /// private bool DecodeHeader() { - int header = input.PeekBits(16); + int header = this.input.PeekBits(16); if (header < 0) { return false; } - input.DropBits(16); + + this.input.DropBits(16); // The header is written in "wrong" byte order header = ((header << 8) | (header >> 8)) & 0xffff; @@ -212,27 +213,28 @@ throw new ImageFormatException("Header checksum illegal"); } - if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) + 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; - */ + * 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; + this.mode = DECODE_BLOCKS; } else { - mode = DECODE_DICT; - neededBits = 32; + this.mode = DECODE_DICT; + this.neededBits = 32; } + return true; } @@ -244,17 +246,19 @@ /// private bool DecodeDict() { - while (neededBits > 0) + while (this.neededBits > 0) { - int dictByte = input.PeekBits(8); + int dictByte = this.input.PeekBits(8); if (dictByte < 0) { return false; } - input.DropBits(8); - readAdler = (readAdler << 8) | dictByte; - neededBits -= 8; + + this.input.DropBits(8); + this.readAdler = (this.readAdler << 8) | dictByte; + this.neededBits -= 8; } + return false; } @@ -270,17 +274,17 @@ /// private bool DecodeHuffman() { - int free = outputWindow.GetFreeSpace(); + int free = this.outputWindow.GetFreeSpace(); while (free >= 258) { int symbol; - switch (mode) + switch (this.mode) { case DECODE_HUFFMAN: // This is the inner loop so it is optimized a bit - while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) + while (((symbol = this.litlenTree.GetSymbol(this.input)) & ~0xff) == 0) { - outputWindow.Write(symbol); + this.outputWindow.Write(symbol); if (--free < 258) { return true; @@ -296,41 +300,44 @@ else { // symbol == 256: end of block - distTree = null; - litlenTree = null; - mode = DECODE_BLOCKS; + this.distTree = null; + this.litlenTree = null; + this.mode = DECODE_BLOCKS; return true; } } try { - repLength = CPLENS[symbol - 257]; - neededBits = CPLEXT[symbol - 257]; + this.repLength = CPLENS[symbol - 257]; + this.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) + if (this.neededBits > 0) { - mode = DECODE_HUFFMAN_LENBITS; - int i = input.PeekBits(neededBits); + this.mode = DECODE_HUFFMAN_LENBITS; + int i = this.input.PeekBits(this.neededBits); if (i < 0) { return false; } - input.DropBits(neededBits); - repLength += i; + + this.input.DropBits(this.neededBits); + this.repLength += i; } - mode = DECODE_HUFFMAN_DIST; + + this.mode = DECODE_HUFFMAN_DIST; goto case DECODE_HUFFMAN_DIST; // fall through case DECODE_HUFFMAN_DIST: - symbol = distTree.GetSymbol(input); + symbol = this.distTree.GetSymbol(this.input); if (symbol < 0) { return false; @@ -338,8 +345,8 @@ try { - repDist = CPDIST[symbol]; - neededBits = CPDEXT[symbol]; + this.repDist = CPDIST[symbol]; + this.neededBits = CPDEXT[symbol]; } catch (Exception) { @@ -349,27 +356,29 @@ goto case DECODE_HUFFMAN_DISTBITS; // fall through case DECODE_HUFFMAN_DISTBITS: - if (neededBits > 0) + if (this.neededBits > 0) { - mode = DECODE_HUFFMAN_DISTBITS; - int i = input.PeekBits(neededBits); + this.mode = DECODE_HUFFMAN_DISTBITS; + int i = this.input.PeekBits(this.neededBits); if (i < 0) { return false; } - input.DropBits(neededBits); - repDist += i; + + this.input.DropBits(this.neededBits); + this.repDist += i; } - outputWindow.Repeat(repLength, repDist); - free -= repLength; - mode = DECODE_HUFFMAN; + this.outputWindow.Repeat(this.repLength, this.repDist); + free -= this.repLength; + this.mode = DECODE_HUFFMAN; break; default: throw new ImageFormatException("Inflater unknown mode"); } } + return true; } @@ -384,24 +393,25 @@ /// private bool DecodeChksum() { - while (neededBits > 0) + while (this.neededBits > 0) { - int chkByte = input.PeekBits(8); + int chkByte = this.input.PeekBits(8); if (chkByte < 0) { return false; } - input.DropBits(8); - readAdler = (readAdler << 8) | chkByte; - neededBits -= 8; + + this.input.DropBits(8); + this.readAdler = (this.readAdler << 8) | chkByte; + this.neededBits -= 8; } - if ((int)adler.Value != readAdler) + if ((int)this.adler.Value != this.readAdler) { - throw new ImageFormatException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler); + throw new ImageFormatException("Adler chksum doesn't match: " + (int)this.adler.Value + " vs. " + this.readAdler); } - mode = FINISHED; + this.mode = FINISHED; return false; } @@ -416,120 +426,129 @@ /// private bool Decode() { - switch (mode) + switch (this.mode) { case DECODE_HEADER: - return DecodeHeader(); + return this.DecodeHeader(); case DECODE_DICT: - return DecodeDict(); + return this.DecodeDict(); case DECODE_CHKSUM: - return DecodeChksum(); + return this.DecodeChksum(); case DECODE_BLOCKS: - if (isLastBlock) + if (this.isLastBlock) { - if (noHeader) + if (this.noHeader) { - mode = FINISHED; + this.mode = FINISHED; return false; } else { - input.SkipToByteBoundary(); - neededBits = 32; - mode = DECODE_CHKSUM; + this.input.SkipToByteBoundary(); + this.neededBits = 32; + this.mode = DECODE_CHKSUM; return true; } } - int type = input.PeekBits(3); + int type = this.input.PeekBits(3); if (type < 0) { return false; } - input.DropBits(3); + + this.input.DropBits(3); if ((type & 1) != 0) { - isLastBlock = true; + this.isLastBlock = true; } + switch (type >> 1) { - case DeflaterConstants.STORED_BLOCK: - input.SkipToByteBoundary(); - mode = DECODE_STORED_LEN1; + case DeflaterConstants.StoredBlock: + this.input.SkipToByteBoundary(); + this.mode = DECODE_STORED_LEN1; break; - case DeflaterConstants.STATIC_TREES: - litlenTree = InflaterHuffmanTree.defLitLenTree; - distTree = InflaterHuffmanTree.defDistTree; - mode = DECODE_HUFFMAN; + case DeflaterConstants.StaticTrees: + this.litlenTree = InflaterHuffmanTree.defLitLenTree; + this.distTree = InflaterHuffmanTree.defDistTree; + this.mode = DECODE_HUFFMAN; break; - case DeflaterConstants.DYN_TREES: - dynHeader = new InflaterDynHeader(); - mode = DECODE_DYN_HEADER; + case DeflaterConstants.DynTrees: + this.dynHeader = new InflaterDynHeader(); + this.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) + if ((this.uncomprLen = this.input.PeekBits(16)) < 0) { return false; } - input.DropBits(16); - mode = DECODE_STORED_LEN2; + + this.input.DropBits(16); + this.mode = DECODE_STORED_LEN2; } + goto case DECODE_STORED_LEN2; // fall through case DECODE_STORED_LEN2: { - int nlen = input.PeekBits(16); + int nlen = this.input.PeekBits(16); if (nlen < 0) { return false; } - input.DropBits(16); - if (nlen != (uncomprLen ^ 0xffff)) + + this.input.DropBits(16); + if (nlen != (this.uncomprLen ^ 0xffff)) { throw new ImageFormatException("broken uncompressed block"); } - mode = DECODE_STORED; + + this.mode = DECODE_STORED; } + goto case DECODE_STORED; // fall through case DECODE_STORED: { - int more = outputWindow.CopyStored(input, uncomprLen); - uncomprLen -= more; - if (uncomprLen == 0) + int more = this.outputWindow.CopyStored(this.input, this.uncomprLen); + this.uncomprLen -= more; + if (this.uncomprLen == 0) { - mode = DECODE_BLOCKS; + this.mode = DECODE_BLOCKS; return true; } - return !input.IsNeedingInput; + + return !this.input.IsNeedingInput; } case DECODE_DYN_HEADER: - if (!dynHeader.Decode(input)) + if (!this.dynHeader.Decode(this.input)) { return false; } - litlenTree = dynHeader.BuildLitLenTree(); - distTree = dynHeader.BuildDistTree(); - mode = DECODE_HUFFMAN; + this.litlenTree = this.dynHeader.BuildLitLenTree(); + this.distTree = this.dynHeader.BuildDistTree(); + this.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(); + return this.DecodeHuffman(); case FINISHED: return false; @@ -550,7 +569,7 @@ /// public void SetDictionary(byte[] buffer) { - SetDictionary(buffer, 0, buffer.Length); + this.SetDictionary(buffer, 0, buffer.Length); } /// @@ -591,20 +610,21 @@ throw new ArgumentOutOfRangeException(nameof(count)); } - if (!IsNeedingDictionary) + if (!this.IsNeedingDictionary) { throw new InvalidOperationException("Dictionary is not needed"); } - adler.Update(buffer, index, count); + this.adler.Update(buffer, index, count); - if ((int)adler.Value != readAdler) + if ((int)this.adler.Value != this.readAdler) { throw new ImageFormatException("Wrong adler checksum"); } - adler.Reset(); - outputWindow.CopyDict(buffer, index, count); - mode = DECODE_BLOCKS; + + this.adler.Reset(); + this.outputWindow.CopyDict(buffer, index, count); + this.mode = DECODE_BLOCKS; } /// @@ -616,7 +636,7 @@ /// public void SetInput(byte[] buffer) { - SetInput(buffer, 0, buffer.Length); + this.SetInput(buffer, 0, buffer.Length); } /// @@ -640,8 +660,8 @@ /// public void SetInput(byte[] buffer, int index, int count) { - input.SetInput(buffer, index, count); - totalIn += (long)count; + this.input.SetInput(buffer, index, count); + this.totalIn += (long)count; } /// @@ -670,7 +690,7 @@ throw new ArgumentNullException(nameof(buffer)); } - return Inflate(buffer, 0, buffer.Length); + return this.Inflate(buffer, 0, buffer.Length); } /// @@ -709,20 +729,12 @@ 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) @@ -733,10 +745,11 @@ // Special case: count may be zero if (count == 0) { - if (!IsFinished) + if (!this.IsFinished) { // -jr- 08-Nov-2003 INFLATE_BUG fix.. - Decode(); + this.Decode(); } + return 0; } @@ -744,22 +757,22 @@ do { - if (mode != DECODE_CHKSUM) + if (this.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); + * checksum in the input stream. + * + * With this trick we have always: + * IsNeedingInput() and not IsFinished() + * implies more output can be produced. + */ + int more = this.outputWindow.CopyOutput(buffer, offset, count); if (more > 0) { - adler.Update(buffer, offset, more); + this.adler.Update(buffer, offset, more); offset += more; bytesCopied += more; - totalOut += (long)more; + this.totalOut += (long)more; count -= more; if (count == 0) { @@ -767,45 +780,28 @@ } } } - } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM))); + } + while (this.Decode() || ((this.outputWindow.GetAvailable() > 0) && (this.mode != DECODE_CHKSUM))); return bytesCopied; } /// /// Returns true, if the input buffer is empty. - /// You should then call setInput(). + /// You should then call setInput(). /// NOTE: This method also returns true when the stream is finished. /// - public bool IsNeedingInput - { - get - { - return input.IsNeedingInput; - } - } + public bool IsNeedingInput => this.input.IsNeedingInput; /// /// Returns true, if a preset dictionary is needed to inflate the input. /// - public bool IsNeedingDictionary - { - get - { - return mode == DECODE_DICT && neededBits == 0; - } - } + public bool IsNeedingDictionary => this.mode == DECODE_DICT && this.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; - } - } + public bool IsFinished => this.mode == FINISHED && this.outputWindow.GetAvailable() == 0; /// /// Gets the adler checksum. This is either the checksum of all @@ -816,13 +812,7 @@ /// /// the adler checksum. /// - public int Adler - { - get - { - return IsNeedingDictionary ? readAdler : (int)adler.Value; - } - } + public int Adler => this.IsNeedingDictionary ? this.readAdler : (int)this.adler.Value; /// /// Gets the total number of output bytes returned by Inflate(). @@ -830,13 +820,7 @@ /// /// the total number of output bytes. /// - public long TotalOut - { - get - { - return totalOut; - } - } + public long TotalOut => this.totalOut; /// /// Gets the total number of processed compressed input bytes. @@ -844,13 +828,7 @@ /// /// The total number of bytes of processed input bytes. /// - public long TotalIn - { - get - { - return totalIn - (long)RemainingInput; - } - } + public long TotalIn => this.totalIn - (long)this.RemainingInput; /// /// Gets the number of unprocessed input bytes. Useful, if the end of the @@ -860,13 +838,6 @@ /// /// 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; - } - } + public int RemainingInput => this.input.AvailableBytes; // TODO: Should this be a long? } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs index 02916ae3d..001558f4a 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs @@ -60,12 +60,6 @@ /// 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); } @@ -77,12 +71,6 @@ /// 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)); } @@ -93,12 +81,6 @@ /// 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)); @@ -113,12 +95,6 @@ /// 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; } @@ -139,12 +115,6 @@ /// 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); @@ -164,16 +134,6 @@ /// 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) @@ -191,12 +151,6 @@ /// 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); } diff --git a/src/ImageProcessor/Formats/Png/Zlib/README.md b/src/ImageProcessor/Formats/Png/Zlib/README.md new file mode 100644 index 000000000..eca351910 --- /dev/null +++ b/src/ImageProcessor/Formats/Png/Zlib/README.md @@ -0,0 +1,2 @@ +The contents of this folder have been copied from https://github.com/ygrenier/SharpZipLib.Portable +in order to allow the project to run the NET 4.6 portable classes. diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 7d32368e7..2fe51042e 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -238,6 +238,7 @@ + diff --git a/src/ImageProcessor/ImageProcessor.csproj.DotSettings b/src/ImageProcessor/ImageProcessor.csproj.DotSettings index 366fb310a..72de168d8 100644 --- a/src/ImageProcessor/ImageProcessor.csproj.DotSettings +++ b/src/ImageProcessor/ImageProcessor.csproj.DotSettings @@ -16,5 +16,6 @@ True True True + True True True \ No newline at end of file diff --git a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs index a1af332e8..f9049056b 100644 --- a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs @@ -17,6 +17,11 @@ Directory.CreateDirectory("Encoded"); } + foreach (FileInfo file in new DirectoryInfo("Encoded").GetFiles()) + { + file.Delete(); + } + foreach (string file in Files) { using (FileStream stream = File.OpenRead(file))