diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index ba404518d..786d02a85 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -14,17 +14,9 @@ namespace ImageProcessor.Formats public class PngEncoder : IImageEncoder { /// - /// The maximum block size. + /// The maximum block size, defaults at 64k for uncompressed blocks. /// - private const int MaxBlockSize = 0xFFFF; - - /// - /// Initializes a new instance of the class. - /// - public PngEncoder() - { - this.Gamma = 2.2f; - } + private const int MaxBlockSize = 65535; /// /// Gets or sets the quality of output for images. @@ -40,40 +32,23 @@ namespace ImageProcessor.Formats /// /// The compression level 1-9. - /// TODO: Get other compression levels to work. Something is cutting of image content. /// Defaults to 6. /// public int CompressionLevel { get; set; } = 6; /// - /// Gets or sets a value indicating whether this encoder - /// will write the image uncompressed the stream. - /// - /// - /// true if the image should be written uncompressed to - /// the stream; otherwise, false. - /// - // TODO: We can't quickly return a color to non-premultiplied with this method. - // Should we remove? - //public bool IsWritingUncompressed { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is writing + /// Gets or sets a value indicating whether this instance should write /// gamma information to the stream. The default value is false. /// - /// - /// True if this instance is writing gamma - /// information to the stream; otherwise, false. - /// - public bool IsWritingGamma { get; set; } + public bool WriteGamma { get; set; } /// /// Gets or sets the gamma value, that will be written - /// the the stream, when the property - /// is set to true. The default value is 2.2f. + /// the the stream, when the property + /// is set to true. The default value is 2.2F. /// /// The gamma value of the image. - public double Gamma { get; set; } + public double Gamma { get; set; } = 2.2F; /// public bool IsSupportedFileExtension(string extension) @@ -111,9 +86,9 @@ namespace ImageProcessor.Formats { Width = image.Width, Height = image.Height, - ColorType = 6, + ColorType = 6, // Each pixel is an R,G,B triple, followed by an alpha sample. BitDepth = 8, - FilterMethod = 0, + FilterMethod = 0, // None CompressionMethod = 0, InterlaceMethod = 0 }; @@ -121,16 +96,7 @@ namespace ImageProcessor.Formats this.WriteHeaderChunk(stream, header); this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - - //if (this.IsWritingUncompressed) - //{ - // this.WriteDataChunksFast(stream, image); - //} - //else - //{ this.WriteDataChunks(stream, image); - //} - this.WriteEndChunk(stream); stream.Flush(); } @@ -187,6 +153,7 @@ namespace ImageProcessor.Formats Image image = imageBase as Image; if (image != null && image.HorizontalResolution > 0 && image.VerticalResolution > 0) { + // 39.3700787 = inches in a meter. int dpmX = (int)Math.Round(image.HorizontalResolution * 39.3700787d); int dpmY = (int)Math.Round(image.VerticalResolution * 39.3700787d); @@ -207,7 +174,7 @@ namespace ImageProcessor.Formats /// The containing image data. private void WriteGammaChunk(Stream stream) { - if (this.IsWritingGamma) + if (this.WriteGamma) { int gammaValue = (int)(this.Gamma * 100000f); @@ -224,78 +191,6 @@ namespace ImageProcessor.Formats } } - /// - /// Writes the pixel information to the stream. - /// - /// The containing image data. - /// The image base. - private void WriteDataChunksFast(Stream stream, ImageBase imageBase) - { - float[] pixels = imageBase.Pixels; - - // Convert the pixel array to a new array for adding - // the filter byte. - byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height]; - - int rowLength = (imageBase.Width * 4) + 1; - - for (int y = 0; y < imageBase.Height; y++) - { - data[y * rowLength] = 0; - Array.Copy(pixels, y * imageBase.Width * 4, data, (y * rowLength) + 1, imageBase.Width * 4); - } - - Adler32 adler32 = new Adler32(); - adler32.Update(data); - - using (MemoryStream tempStream = new MemoryStream()) - { - int remainder = data.Length; - - int blockCount; - if ((data.Length % MaxBlockSize) == 0) - { - blockCount = data.Length / MaxBlockSize; - } - else - { - blockCount = (data.Length / MaxBlockSize) + 1; - } - - // Write headers - tempStream.WriteByte(0x78); - tempStream.WriteByte(0xDA); - - for (int i = 0; i < blockCount; i++) - { - // Write the length - ushort length = (ushort)((remainder < MaxBlockSize) ? remainder : MaxBlockSize); - - tempStream.WriteByte(length == remainder ? (byte)0x01 : (byte)0x00); - - tempStream.Write(BitConverter.GetBytes(length), 0, 2); - - // Write one's compliment of length - tempStream.Write(BitConverter.GetBytes((ushort)~length), 0, 2); - - // Write blocks - tempStream.Write(data, i * MaxBlockSize, length); - - // Next block - remainder -= MaxBlockSize; - } - - WriteInteger(tempStream, (int)adler32.Value); - - tempStream.Seek(0, SeekOrigin.Begin); - - byte[] zipData = new byte[tempStream.Length]; - tempStream.Read(zipData, 0, (int)tempStream.Length); - - this.WriteChunk(stream, PngChunkTypes.Data, zipData); - } - } - /// /// Writes the pixel information to the stream. /// @@ -367,17 +262,13 @@ namespace ImageProcessor.Formats { memoryStream = new MemoryStream(); - // TODO: Get this working! - //using (ZlibOutputStream outputStream = new ZlibOutputStream(memoryStream, this.CompressionLevel)) - using (DeflaterOutputStream outputStream = new DeflaterOutputStream(memoryStream)) + using (ZlibOutputStream outputStream = new ZlibOutputStream(memoryStream, this.CompressionLevel)) { outputStream.Write(data, 0, data.Length); - outputStream.Flush(); - outputStream.Finish(); - - bufferLength = (int)memoryStream.Length; - buffer = memoryStream.ToArray(); } + + bufferLength = (int)memoryStream.Length; + buffer = memoryStream.ToArray(); } finally { diff --git a/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs index 12cd0ca9f..25bc74853 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Adler32.cs @@ -52,9 +52,9 @@ namespace ImageProcessor.Formats /// checked separately. (Any sequence of zeroes has a Fletcher /// checksum of zero.)" /// - /// - /// - public sealed class Adler32 : IChecksum + /// + /// + internal sealed class Adler32 : IChecksum { /// /// largest prime smaller than 65536 @@ -67,21 +67,18 @@ namespace ImageProcessor.Formats private uint checksum; /// - /// Initializes a new instance of the 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. - /// + /// public void Reset() { this.checksum = 1; @@ -106,12 +103,7 @@ namespace ImageProcessor.Formats this.checksum = (s2 << 16) + s1; } - /// - /// Updates the checksum with an array of bytes. - /// - /// - /// The source of the data to update with. - /// + /// public void Update(byte[] buffer) { if (buffer == null) @@ -122,18 +114,7 @@ namespace ImageProcessor.Formats this.Update(buffer, 0, buffer.Length); } - /// - /// Updates the checksum with the bytes taken from the array. - /// - /// - /// an array of bytes - /// - /// - /// the start of the data used for this update - /// - /// - /// the number of bytes to use for this update - /// + /// public void Update(byte[] buffer, int offset, int count) { if (buffer == null) diff --git a/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs b/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs index 70cceb0a2..d2a3e6670 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/Crc32.cs @@ -12,6 +12,7 @@ namespace ImageProcessor.Formats /// 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 @@ -19,7 +20,8 @@ namespace ImageProcessor.Formats /// polynomial q, also with the lowest power in the most significant bit (so the /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, /// where a mod b means the remainder after dividing a by b. - /// + /// + /// /// This calculation is done using the shift-register method of multiplying and /// taking the remainder. The register is initialized to zero, and for each /// incoming bit, x^32 is added mod p to the register if the bit is a one (where @@ -27,15 +29,17 @@ namespace ImageProcessor.Formats /// x (which is shifting right by one and adding x^32 mod p if the bit shifted /// out is a one). We start with the highest power (least significant bit) of /// q and repeat for all eight bits of q. - /// + /// + /// /// The table is simply the CRC of all possible eight bit values. This is all /// the information needed to generate CRC's on data a byte at a time for all /// combinations of CRC register values and incoming bytes. + /// /// - public sealed class Crc32 : IChecksum + internal sealed class Crc32 : IChecksum { /// - /// The crc seed + /// The cycle redundancy check seed /// private const uint CrcSeed = 0xFFFFFFFF; @@ -99,13 +103,11 @@ namespace ImageProcessor.Formats }; /// - /// The crc data checksum so far. + /// The data checksum so far. /// private uint crc; - /// - /// Gets or sets the CRC32 data checksum computed so far. - /// + /// public long Value { get @@ -119,16 +121,14 @@ namespace ImageProcessor.Formats } } - /// - /// Resets the CRC32 data checksum as if no update was ever called. - /// + /// public void Reset() { this.crc = 0; } /// - /// Updates the checksum with the int bval. + /// Updates the checksum with the given value. /// /// The byte is taken as the lower 8 bits of value. public void Update(int value) @@ -138,12 +138,7 @@ namespace ImageProcessor.Formats this.crc ^= CrcSeed; } - /// - /// Updates the checksum with the bytes taken from the array. - /// - /// - /// buffer an array of bytes - /// + /// public void Update(byte[] buffer) { if (buffer == null) @@ -154,18 +149,7 @@ namespace ImageProcessor.Formats this.Update(buffer, 0, buffer.Length); } - /// - /// Adds the byte array to the data checksum. - /// - /// - /// The buffer which contains the data - /// - /// - /// The offset in the buffer where the data starts - /// - /// - /// The number of data bytes to update the CRC with. - /// + /// public void Update(byte[] buffer, int offset, int count) { if (buffer == null) @@ -192,16 +176,5 @@ 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 deleted file mode 100644 index f3655af8b..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflateStrategy.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - /// - /// Strategies for deflater - /// - public enum DeflateStrategy - { - /// - /// The default strategy - /// - Default = 0, - - /// - /// This strategy will only allow longer string repetitions. It is - /// useful for random data with a small character set. - /// - Filtered = 1, - - /// - /// This strategy will not look for string repetitions at all. It - /// only encodes with Huffman trees (which means, that more common - /// characters get a smaller encoding. - /// - HuffmanOnly = 2 - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs deleted file mode 100644 index eccbab825..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/Deflater.cs +++ /dev/null @@ -1,561 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System; - - /// - /// This is the Deflater class. The deflater class compresses input - /// with the deflate algorithm described in RFC 1951. It has several - /// compression levels and three different strategies described below. - /// - /// This class is not thread safe. This is inherent in the API, due - /// to the split of deflate and setInput. - /// - /// author of the original java version : Jochen Hoenicke - /// - public class Deflater - { - /* - * The Deflater can do the following state transitions: - * - * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. - * / | (2) (5) | - * / v (5) | - * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) - * \ | (3) | ,--------' - * | | | (3) / - * v v (5) v v - * (1) -> BUSY_STATE ----> FINISHING_STATE - * | (6) - * v - * FINISHED_STATE - * \_____________________________________/ - * | (7) - * v - * CLOSED_STATE - * - * (1) If we should produce a header we start in INIT_STATE, otherwise - * we start in BUSY_STATE. - * (2) A dictionary may be set only when we are in INIT_STATE, then - * we change the state as indicated. - * (3) Whether a dictionary is set or not, on the first call of deflate - * we change to BUSY_STATE. - * (4) -- intentionally left blank -- :) - * (5) FINISHING_STATE is entered, when flush() is called to indicate that - * there is no more INPUT. There are also states indicating, that - * the header wasn't written yet. - * (6) FINISHED_STATE is entered, when everything has been flushed to the - * internal pending output buffer. - * (7) At any time (7) - * - */ - - /// - /// The best and slowest compression level. This tries to find very - /// long and distant string repetitions. - /// - public const int BestCompression = 9; - - /// - /// The worst but fastest compression level. - /// - public const int BestSpeed = 1; - - /// - /// The default compression level. - /// - public const int DefaultCompression = -1; - - /// - /// This level won't compress at all but output uncompressed blocks. - /// - 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; - - /// - /// 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) - { - } - - /// - /// Initializes a new instance of the class with the given compressin level. - /// - /// - /// The compression level, a value between NoCompression and BestCompression, or DefaultCompression. - /// - /// If level is out of range. - public Deflater(int level) - : this(level, false) - { - } - - /// - /// Initializes a new instance of the class with the given compressin level. - /// - /// - /// The compression level, a value between NoCompression and BestCompression, or DefaultCompression. - /// - /// - /// 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 == DefaultCompression) - { - level = 6; - } - else if (level < NoCompression || level > BestCompression) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - this.pending = new DeflaterPending(); - this.engine = new DeflaterEngine(this.pending); - this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; - 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 => this.engine.Adler; - - /// - /// Gets the number of input bytes processed so far. - /// - public long TotalIn => this.engine.TotalIn; - - /// - /// Gets the number of output bytes so far. - /// - 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() - { - this.state = this.noZlibHeaderOrFooter ? BusyState : InitState; - this.totalOut = 0; - this.pending.Reset(); - this.engine.Reset(); - } - - /// - /// Flushes the current input block. Further calls to deflate() will - /// produce enough output to inflate everything in the current input - /// block. This is not part of Sun's JDK so I have made it package - /// private. It is used by DeflaterOutputStream to implement - /// flush(). - /// - public void Flush() - { - this.state |= IsFlushing; - } - - /// - /// Finishes the deflater with the current input block. It is an error - /// to give more input after this method was called. This method must - /// be called to force all bytes to be flushed. - /// - public void Finish() - { - this.state |= IsFlushing | IsFinishing; - } - - /// - /// Sets the data which should be compressed next. This should be only - /// called when needsInput indicates that more input is needed. - /// If you call setInput when needsInput() returns false, the - /// previous input that is still pending will be thrown away. - /// The given byte array should not be changed, before needsInput() returns - /// true again. - /// This call is equivalent to setInput(input, 0, input.length). - /// - /// - /// the buffer containing the input data. - /// - /// - /// if the buffer was finished() or ended(). - /// - public void SetInput(byte[] input) - { - this.SetInput(input, 0, input.Length); - } - - /// - /// Sets the data which should be compressed next. This should be - /// only called when needsInput indicates that more input is needed. - /// The given byte array should not be changed, before needsInput() returns - /// true again. - /// - /// - /// the buffer containing the input data. - /// - /// - /// the start of the data. - /// - /// - /// the number of data bytes of input. - /// - /// - /// if the buffer was Finish()ed or if previous input is still pending. - /// - public void SetInput(byte[] input, int offset, int count) - { - if ((this.state & IsFinishing) != 0) - { - throw new InvalidOperationException("Finish() already called"); - } - - this.engine.SetInput(input, offset, count); - } - - /// - /// Sets the compression level. There is no guarantee of the exact - /// position of the change, but if you call this when needsInput is - /// true the change of compression level will occur somewhere near - /// before the end of the so far given input. - /// - /// - /// the new compression level. - /// - public void SetLevel(int level) - { - if (level == DefaultCompression) - { - level = 6; - } - else if (level < NoCompression || level > BestCompression) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - if (this.deflaterLevel != level) - { - this.deflaterLevel = level; - this.engine.SetLevel(level); - } - } - - /// - /// Get current compression level - /// - /// Returns the current compression level - public int GetLevel() - { - return this.deflaterLevel; - } - - /// - /// Sets the compression strategy. Strategy is one of - /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact - /// position where the strategy is changed, the same as for - /// SetLevel() applies. - /// - /// - /// The new compression strategy. - /// - public void SetStrategy(DeflateStrategy strategy) - { - this.engine.Strategy = strategy; - } - - /// - /// Deflates the current input block with to the given array. - /// - /// - /// The buffer where compressed data is stored - /// - /// - /// The number of compressed bytes added to the output, or 0 if either - /// IsNeedingInput() or IsFinished returns true or length is zero. - /// - public int Deflate(byte[] output) - { - return this.Deflate(output, 0, output.Length); - } - - /// - /// Deflates the current input block to the given array. - /// - /// - /// Buffer to store the compressed data. - /// - /// - /// Offset into the output array. - /// - /// - /// The maximum number of bytes that may be stored. - /// - /// - /// The number of compressed bytes added to the output, or 0 if either - /// needsInput() or finished() returns true or length is zero. - /// - /// - /// If Finish() was previously called. - /// - /// - /// If offset or length don't match the array length. - /// - public int Deflate(byte[] output, int offset, int length) - { - int origLength = length; - - if (this.state == ClosedState) - { - throw new InvalidOperationException("Deflater closed"); - } - - if (this.state < BusyState) - { - // output header - int header = (Deflated + - ((DeflaterConstants.MaxWbits - 8) << 4)) << 8; - int levelFlags = (this.deflaterLevel - 1) >> 1; - if (levelFlags < 0 || levelFlags > 3) - { - levelFlags = 3; - } - - header |= levelFlags << 6; - if ((this.state & IsSetdict) != 0) - { - // Dictionary was set - header |= DeflaterConstants.PresetDict; - } - - header += 31 - (header % 31); - - this.pending.WriteShortMSB(header); - if ((this.state & IsSetdict) != 0) - { - int chksum = this.engine.Adler; - this.engine.ResetAdler(); - this.pending.WriteShortMSB(chksum >> 16); - this.pending.WriteShortMSB(chksum & 0xffff); - } - - this.state = BusyState | (this.state & (IsFlushing | IsFinishing)); - } - - for (; ;) - { - int count = this.pending.Flush(output, offset, length); - offset += count; - this.totalOut += count; - length -= count; - - if (length == 0 || this.state == FinishedState) - { - break; - } - - if (!this.engine.Deflate((this.state & IsFlushing) != 0, (this.state & IsFinishing) != 0)) - { - if (this.state == BusyState) - { - // We need more input now - return origLength - length; - } - else if (this.state == FlushingState) - { - 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 + ((-this.pending.BitCount) & 7); - while (neededbits > 0) - { - /* write a static tree block consisting solely of - * an EOF: - */ - this.pending.WriteBits(2, 10); - neededbits -= 10; - } - } - - this.state = BusyState; - } - else if (this.state == FinishingState) - { - this.pending.AlignToByte(); - - // Compressed data is complete. Write footer information if required. - if (!this.noZlibHeaderOrFooter) - { - int adler = this.engine.Adler; - this.pending.WriteShortMSB(adler >> 16); - this.pending.WriteShortMSB(adler & 0xffff); - } - - this.state = FinishedState; - } - } - } - - return origLength - length; - } - - /// - /// Sets the dictionary which should be used in the deflate process. - /// This call is equivalent to setDictionary(dict, 0, dict.Length). - /// - /// - /// the dictionary. - /// - /// - /// if SetInput () or Deflate () were already called or another dictionary was already set. - /// - public void SetDictionary(byte[] dictionary) - { - this.SetDictionary(dictionary, 0, dictionary.Length); - } - - /// - /// Sets the dictionary which should be used in the deflate process. - /// The dictionary is a byte array containing strings that are - /// likely to occur in the data which should be compressed. The - /// dictionary is not stored in the compressed output, only a - /// checksum. To decompress the output you need to supply the same - /// dictionary again. - /// - /// - /// The dictionary data - /// - /// - /// The index where dictionary information commences. - /// - /// - /// The number of bytes in the dictionary. - /// - /// - /// If SetInput () or Deflate() were already called or another dictionary was already set. - /// - public void SetDictionary(byte[] dictionary, int index, int count) - { - if (this.state != InitState) - { - throw new InvalidOperationException(); - } - - this.state = SetdictState; - this.engine.SetDictionary(dictionary, index, count); - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs deleted file mode 100644 index 8f333f5f6..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterConstants.cs +++ /dev/null @@ -1,145 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System; - - /// - /// This class contains constants used for deflation. - /// - public class DeflaterConstants - { - /// - /// Written to Zip file to identify a stored block - /// - public const int StoredBlock = 0; - - /// - /// Identifies static tree in Zip file - /// - public const int StaticTrees = 1; - - /// - /// Identifies dynamic tree in Zip file - /// - public const int DynTrees = 2; - - /// - /// Header flag indicating a preset dictionary for deflation - /// - public const int PresetDict = 0x20; - - /// - /// Sets internal buffer sizes for Huffman encoding - /// - public const int DefaultMemLevel = 8; - - /// - /// Internal compression engine constant - /// - public const int MaxMatch = 258; - - /// - /// Internal compression engine constant - /// - public const int MinMatch = 3; - - /// - /// Internal compression engine constant - /// - public const int MaxWbits = 15; - - /// - /// Internal compression engine constant - /// - public const int Wsize = 1 << MaxWbits; - - /// - /// Internal compression engine constant - /// - public const int Wmask = Wsize - 1; - - /// - /// Internal compression engine constant - /// - public const int HashBits = DefaultMemLevel + 7; - - /// - /// Internal compression engine constant - /// - public const int HashSize = 1 << HashBits; - - /// - /// Internal compression engine constant - /// - public const int HashMask = HashSize - 1; - - /// - /// Internal compression engine constant - /// - public const int HashShift = (HashBits + MinMatch - 1) / MinMatch; - - /// - /// Internal compression engine constant - /// - public const int MinLookahead = MaxMatch + MinMatch + 1; - - /// - /// Internal compression engine constant - /// - public const int MaxDist = Wsize - MinLookahead; - - /// - /// Internal compression engine constant - /// - public const int PendingBufSize = 1 << (DefaultMemLevel + 8); - - /// - /// Internal compression engine constant - /// - public const int Deflatestored = 0; - - /// - /// Internal compression engine constant - /// - public const int Deflatefast = 1; - - /// - /// Internal compression engine constant - /// - public const int Deflateslow = 2; - - /// - /// Internal compression engine constant - /// - public static int MaxBlockSize => Math.Min(65535, PendingBufSize - 5); - - /// - /// Internal compression engine constant - /// - public static int[] GoodLength => new[] { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; - - /// - /// Internal compression engine constant - /// - public static int[] MaxLazy => new[] { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; - - /// - /// Internal compression engine constant - /// - public static int[] NiceLength => new[] { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; - - /// - /// Internal compression engine constant - /// - public static int[] MaxChain => new[] { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; - - /// - /// Internal compression engine constant - /// - 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 deleted file mode 100644 index 1b5da2715..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterEngine.cs +++ /dev/null @@ -1,810 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System; - - /// - /// Low level compression engine for deflate algorithm which uses a 32K sliding window - /// with secondary compression from Huffman/Shannon-Fano codes. - /// - /// - /// DEFLATE ALGORITHM: - /// - /// The uncompressed stream is inserted into the window array. When - /// the window array is full the first half is thrown away and the - /// second half is copied to the beginning. - /// - /// The head array is a hash table. Three characters build a hash value - /// and they the value points to the corresponding index in window of - /// the last string with this hash. The prev array implements a - /// linked list of matches with the same hash: prev[index & WMASK] points - /// to the previous index with the same hash. - /// - public class DeflaterEngine : DeflaterConstants - { - /// - /// One more than the maximum upper bounds. - /// - private const int TooFar = 4096; - - /// - /// prev[index & WMASK] points to the previous index that has the - /// same hash code as the string starting at index. This way - /// entries with the same hash code are in a linked list. - /// Note that the array should really be unsigned short, so you need - /// to and the values with 0xffff. - /// - private readonly short[] previousIndex; - - /// - /// Hashtable, hashing three characters to an index for window, so - /// that window[index]..window[index+2] have this hash code. - /// Note that the array should really be unsigned short, so you need - /// to and the values with 0xffff. - /// - private readonly short[] head; - - /// - /// 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. - /// - /// The pending buffer to use> - public DeflaterEngine(DeflaterPending pending) - { - this.pending = pending; - this.huffman = new DeflaterHuffman(pending); - this.adler = new Adler32(); - - this.window = new byte[2 * Wsize]; - this.head = new short[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. - this.blockStart = this.strstart = 1; - } - - /// - /// Get current value of Adler checksum - /// - public int Adler => unchecked((int)this.adler.Value); - - /// - /// Total data processed - /// - public long TotalIn => this.totalIn; - - /// - /// Get or sets the - /// - public DeflateStrategy Strategy { get; set; } - - /// - /// Deflate drives actual compression of data - /// - /// True to flush input buffers - /// Finish deflation with the current input. - /// Returns true if progress has been made. - public bool Deflate(bool flush, bool finish) - { - bool progress; - do - { - this.FillWindow(); - bool canFlush = flush && (this.inputOff == this.inputEnd); - - switch (this.compressionFunction) - { - case Deflatestored: - progress = this.DeflateStored(canFlush, finish); - break; - case Deflatefast: - progress = this.DeflateFast(canFlush, finish); - break; - case Deflateslow: - progress = this.DeflateSlow(canFlush, finish); - break; - default: - throw new InvalidOperationException("unknown compressionFunction"); - } - } - while (this.pending.IsFlushed && progress); // repeat while we have no pending output and progress was made - return progress; - } - - /// - /// Sets input data to be deflated. Should only be called when NeedsInput() - /// returns true - /// - /// The buffer containing input data. - /// The offset of the first byte of data. - /// The number of bytes of data to use as input. - public void SetInput(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (this.inputOff < this.inputEnd) - { - throw new InvalidOperationException("Old input was not completely processed"); - } - - int end = offset + count; - - // We want to throw an ArrayIndexOutOfBoundsException early. The - // check is very tricky: it also handles integer wrap around. - if ((offset > end) || (end > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - this.inputBuf = buffer; - this.inputOff = offset; - this.inputEnd = end; - } - - /// - /// Determines if more input is needed. - /// - /// Return true if input is needed via SetInput - public bool NeedsInput() - { - return this.inputEnd == this.inputOff; - } - - /// - /// Set compression dictionary - /// - /// The buffer containing the dictionary data - /// The offset in the buffer for the first byte of data - /// The length of the dictionary data. - public void SetDictionary(byte[] buffer, int offset, int length) - { - this.adler.Update(buffer, offset, length); - if (length < MinMatch) - { - return; - } - - if (length > MaxDist) - { - offset += length - MaxDist; - length = MaxDist; - } - - Array.Copy(buffer, offset, this.window, this.strstart, length); - - this.UpdateHash(); - --length; - while (--length > 0) - { - this.InsertString(); - this.strstart++; - } - - this.strstart += 2; - this.blockStart = this.strstart; - } - - /// - /// Reset internal state - /// - public void Reset() - { - this.huffman.Reset(); - this.adler.Reset(); - this.blockStart = this.strstart = 1; - this.lookahead = 0; - this.totalIn = 0; - this.prevAvailable = false; - this.matchLen = MinMatch - 1; - - for (int i = 0; i < HashSize; i++) - { - this.head[i] = 0; - } - - for (int i = 0; i < Wsize; i++) - { - this.previousIndex[i] = 0; - } - } - - /// - /// Reset Adler checksum - /// - public void ResetAdler() - { - this.adler.Reset(); - } - - /// - /// Set the deflate level (0-9) - /// - /// The value to set the level to. - public void SetLevel(int level) - { - if ((level < 0) || (level > 9)) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - this.goodLength = GoodLength[level]; - this.maxLazy = MaxLazy[level]; - this.niceLength = NiceLength[level]; - this.maxChain = MaxChain[level]; - - if (ComprFunc[level] != this.compressionFunction) - { - switch (this.compressionFunction) - { - case Deflatestored: - if (this.strstart > this.blockStart) - { - this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); - this.blockStart = this.strstart; - } - - this.UpdateHash(); - break; - - case Deflatefast: - if (this.strstart > this.blockStart) - { - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); - this.blockStart = this.strstart; - } - - break; - - case Deflateslow: - if (this.prevAvailable) - { - this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff); - } - - if (this.strstart > this.blockStart) - { - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); - this.blockStart = this.strstart; - } - - this.prevAvailable = false; - this.matchLen = MinMatch - 1; - break; - } - - this.compressionFunction = ComprFunc[level]; - } - } - - /// - /// 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 + MaxDist) - { - this.SlideWindow(); - } - - // If there is not enough lookahead, but still some input left, - // read in the input - while (this.lookahead < MinLookahead && this.inputOff < this.inputEnd) - { - int more = (2 * Wsize) - this.lookahead - this.strstart; - - if (more > this.inputEnd - this.inputOff) - { - more = this.inputEnd - this.inputOff; - } - - Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more); - this.adler.Update(this.inputBuf, this.inputOff, more); - - this.inputOff += more; - this.totalIn += more; - this.lookahead += more; - } - - if (this.lookahead >= MinMatch) - { - this.UpdateHash(); - } - } - - /// - /// Updates this hash. - /// - private void UpdateHash() - { - this.insertHashIndex = (this.window[this.strstart] << HashShift) ^ this.window[this.strstart + 1]; - } - - /// - /// Inserts the current string in the head hash and returns the previous - /// value for this hash. - /// - /// The previous hash value - private int InsertString() - { - short match; - int hash = ((this.insertHashIndex << HashShift) ^ this.window[this.strstart + (MinMatch - 1)]) & HashMask; - - 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; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). - for (int i = 0; i < HashSize; ++i) - { - int m = this.head[i] & 0xffff; - this.head[i] = (short)(m >= Wsize ? (m - Wsize) : 0); - } - - // Slide the prev table. - for (int i = 0; i < Wsize; i++) - { - int m = this.previousIndex[i] & 0xffff; - this.previousIndex[i] = (short)(m >= Wsize ? (m - Wsize) : 0); - } - } - - /// - /// Find the best (longest) string in the window matching the - /// string starting at strstart. - /// - /// Preconditions: - /// - /// strstart + MAX_MATCH <= window.length. - /// - /// The current match. - /// True if a match greater than the minimum length is found - private bool FindLongestMatch(int curMatch) - { - int chainLength = this.maxChain; - int length = this.niceLength; - short[] previous = this.previousIndex; - int scan = this.strstart; - int bestEnd = this.strstart + this.matchLen; - int bestLength = Math.Max(this.matchLen, MinMatch - 1); - - int limit = Math.Max(this.strstart - MaxDist, 0); - - int strend = this.strstart + MaxMatch - 1; - byte scanEnd1 = this.window[bestEnd - 1]; - byte scanEnd = this.window[bestEnd]; - - // Do not waste too much time if we already have a good match: - if (bestLength >= this.goodLength) - { - chainLength >>= 2; - } - - // Do not look for matches beyond the end of the input. This is necessary - // to make deflate deterministic. - if (length > this.lookahead) - { - length = this.lookahead; - } - - do - { - if (this.window[curMatch + bestLength] != scanEnd || - this.window[curMatch + bestLength - 1] != scanEnd1 || - this.window[curMatch] != this.window[scan] || - this.window[curMatch + 1] != this.window[scan + 1]) - { - continue; - } - - int match = curMatch + 2; - scan += 2; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart + 258. - while ( - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - this.window[++scan] == this.window[++match] && - (scan < strend)) - { - // Do nothing - } - - if (scan > bestEnd) - { - this.matchStart = curMatch; - bestEnd = scan; - bestLength = scan - this.strstart; - - if (bestLength >= length) - { - break; - } - - scanEnd1 = this.window[bestEnd - 1]; - scanEnd = this.window[bestEnd]; - } - - scan = this.strstart; - } - while ((curMatch = previous[curMatch & Wmask] & 0xffff) > limit && --chainLength != 0); - - this.matchLen = Math.Min(bestLength, this.lookahead); - return this.matchLen >= 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)) - { - return false; - } - - this.strstart += this.lookahead; - this.lookahead = 0; - - int storedLength = this.strstart - this.blockStart; - - if ((storedLength >= MaxBlockSize) || // Block is full - (this.blockStart < Wsize && storedLength >= MaxDist) || // Block may move out of window - flush) - { - bool lastBlock = finish; - if (storedLength > MaxBlockSize) - { - storedLength = MaxBlockSize; - lastBlock = false; - } - - this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock); - this.blockStart += storedLength; - return !lastBlock; - } - - 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 < MinLookahead && !flush) - { - return false; - } - - while (this.lookahead >= MinLookahead || flush) - { - if (this.lookahead == 0) - { - // We are flushing everything - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish); - this.blockStart = this.strstart; - return false; - } - - if (this.strstart > (2 * Wsize) - MinLookahead) - { - // 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 >= MinMatch && - (hashHead = this.InsertString()) != 0 && - this.Strategy != DeflateStrategy.HuffmanOnly && - 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 >= MinMatch) - { - while (--this.matchLen > 0) - { - ++this.strstart; - this.InsertString(); - } - - ++this.strstart; - } - else - { - this.strstart += this.matchLen; - if (this.lookahead >= MinMatch - 1) - { - this.UpdateHash(); - } - } - - this.matchLen = MinMatch - 1; - if (!full) - { - continue; - } - } - else - { - // No match found - this.huffman.TallyLit(this.window[this.strstart] & 0xff); - ++this.strstart; - --this.lookahead; - } - - if (this.huffman.IsFull()) - { - bool lastBlock = finish && (this.lookahead == 0); - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock); - this.blockStart = this.strstart; - return !lastBlock; - } - } - - return true; - } - - /// - /// 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 < MinLookahead && !flush) - { - return false; - } - - while (this.lookahead >= MinLookahead || flush) - { - if (this.lookahead == 0) - { - if (this.prevAvailable) - { - this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff); - } - - this.prevAvailable = false; - - // We are flushing everything - this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish); - this.blockStart = this.strstart; - return false; - } - - if (this.strstart >= (2 * Wsize) - MinLookahead) - { - // slide window, as FindLongestMatch needs this. - // This should only happen when flushing and the window - // is almost full. - this.SlideWindow(); - } - - int prevMatch = this.matchStart; - int prevLen = this.matchLen; - if (this.lookahead >= MinMatch) - { - int hashHead = this.InsertString(); - - if (this.Strategy != DeflateStrategy.HuffmanOnly && - hashHead != 0 && - 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 == MinMatch && this.strstart - this.matchStart > TooFar))) - { - this.matchLen = MinMatch - 1; - } - } - } - - // previous match was better - if ((prevLen >= MinMatch) && (this.matchLen <= prevLen)) - { - this.huffman.TallyDist(this.strstart - 1 - prevMatch, prevLen); - prevLen -= 2; - do - { - this.strstart++; - this.lookahead--; - if (this.lookahead >= MinMatch) - { - this.InsertString(); - } - } - while (--prevLen > 0); - - this.strstart++; - this.lookahead--; - this.prevAvailable = false; - this.matchLen = MinMatch - 1; - } - else - { - if (this.prevAvailable) - { - this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff); - } - - this.prevAvailable = true; - this.strstart++; - this.lookahead--; - } - - if (this.huffman.IsFull()) - { - int len = this.strstart - this.blockStart; - if (this.prevAvailable) - { - len--; - } - - bool lastBlock = finish && (this.lookahead == 0) && !this.prevAvailable; - this.huffman.FlushBlock(this.window, this.blockStart, len, lastBlock); - this.blockStart += len; - return !lastBlock; - } - } - - return true; - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs deleted file mode 100644 index 5d6857d41..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterHuffman.cs +++ /dev/null @@ -1,889 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - /// - /// This is the DeflaterHuffman class. - /// - /// This class is not thread safe. This is inherent in the API, due - /// to the split of Deflate and SetInput. - /// - /// author of the original java version : Jochen Hoenicke - /// - public class DeflaterHuffman - { - /// - /// The buffer size. - /// - private const int Buffersize = 1 << (DeflaterConstants.DefaultMemLevel + 6); - - /// - /// The number of literals. - /// - private const int LiteralCount = 286; - - /// - /// Number of distance codes - /// - private const int DistanceCodeCount = 30; - - /// - /// Number of codes used to transfer bit lengths - /// - private const int BitLengthCount = 19; - - /// - /// Repeat previous bit length 3-6 times (2 bits of repeat count) - /// - private const int Repeat3To6 = 16; - - /// - /// Repeat a zero length 3-10 times (3 bits of repeat count) - /// - private const int Repeat3To10 = 17; - - /// - /// Repeat a zero length 11-138 times (7 bits of repeat count) - /// - private const int Repeat11To138 = 18; - - /// - /// The end of file flag. - /// - private const int Eof = 256; - - /// - /// The lengths of the bit length codes are sent in order of decreasing - /// probability, to avoid transmitting the lengths for unused bit length codes. - /// - private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - /// - /// 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; - - /// - /// 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.deflater = huffman; - this.minimumNumberOfCodes = minCodes; - this.maxLength = maxLength; - this.Frequencies = new short[elems]; - this.blCounts = new int[maxLength]; - } - - /// - /// 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 < this.Frequencies.Length; i++) - { - this.Frequencies[i] = 0; - } - - this.codes = null; - this.Length = null; - } - - /// - /// Writes a code symbol. - /// - /// The code index. - public void WriteSymbol(int code) - { - this.deflater.pending.WriteBits(this.codes[code] & 0xffff, this.Length[code]); - } - - /// - /// Set static codes and length - /// - /// new codes - /// length for new codes - public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) - { - this.codes = staticCodes; - this.Length = staticLengths; - } - - /// - /// Build dynamic codes and lengths - /// - public void BuildCodes() - { - int numSymbols = this.Frequencies.Length; - int[] nextCode = new int[this.maxLength]; - int code = 0; - - this.codes = new short[numSymbols]; - - for (int bits = 0; bits < this.maxLength; bits++) - { - nextCode[bits] = code; - code += this.blCounts[bits] << (15 - bits); - } - - for (int i = 0; i < this.NumberOfCodes; i++) - { - int bits = this.Length[i]; - if (bits > 0) - { - this.codes[i] = BitReverse(nextCode[bits - 1]); - nextCode[bits - 1] += 1 << (16 - bits); - } - } - } - - public void BuildTree() - { - 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 = this.Frequencies[n]; - if (freq != 0) - { - // Insert n into heap - int pos = heapLen++; - int ppos; - 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. - while (heapLen < 2) - { - int node = maxCode < 2 ? ++maxCode : 0; - heap[heapLen++] = node; - } - - 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 numNodes = numLeafs; - for (int i = 0; i < heapLen; i++) - { - int node = heap[i]; - childs[2 * i] = node; - 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. - do - { - int first = heap[0]; - int last = heap[--heapLen]; - - // Propagate the hole to the leafs of the heap - int ppos = 0; - int path = 1; - - while (path < heapLen) - { - if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) - { - path++; - } - - heap[ppos] = heap[path]; - ppos = path; - path = (path * 2) + 1; - } - - // Now propagate the last element down along path. Normally - // it shouldn't go too deep. - int lastVal = values[last]; - while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) - { - heap[path] = heap[ppos]; - } - - heap[path] = last; - - int second = heap[0]; - - // Create a new node father of first and second - last = numNodes++; - childs[2 * last] = first; - childs[(2 * last) + 1] = second; - int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); - values[last] = lastVal = values[first] + values[second] - mindepth + 1; - - // Again, propagate the hole to the leafs - ppos = 0; - path = 1; - - while (path < heapLen) - { - if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) - { - path++; - } - - heap[ppos] = heap[path]; - ppos = path; - path = (ppos * 2) + 1; - } - - // Now propagate the new element down along path - while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) - { - heap[path] = heap[ppos]; - } - - heap[path] = last; - } - while (heapLen > 1); - - if (heap[0] != (childs.Length / 2) - 1) - { - throw new ImageFormatException("Heap invariant violated"); - } - - this.BuildLength(childs); - } - - /// - /// Get encoded length - /// - /// Encoded length, the sum of frequencies * lengths - public int GetEncodedLength() - { - int len = 0; - for (int i = 0; i < this.Frequencies.Length; i++) - { - len += this.Frequencies[i] * this.Length[i]; - } - - return len; - } - - /// - /// Scan a literal or distance tree to determine the frequencies of the codes - /// in the bit length tree. - /// - public void CalcBLFreq(Tree blTree) - { - int max_count; /* max repeat count */ - int min_count; /* min repeat count */ - int count; /* repeat count of the current code */ - int curlen = -1; /* length of current code */ - - int i = 0; - while (i < this.NumberOfCodes) - { - count = 1; - int nextlen = this.Length[i]; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else - { - max_count = 6; - min_count = 3; - if (curlen != nextlen) - { - blTree.Frequencies[nextlen]++; - count = 0; - } - } - - curlen = nextlen; - i++; - - while (i < this.NumberOfCodes && curlen == this.Length[i]) - { - i++; - if (++count >= max_count) - { - break; - } - } - - if (count < min_count) - { - blTree.Frequencies[curlen] += (short)count; - } - else if (curlen != 0) - { - blTree.Frequencies[Repeat3To6]++; - } - else if (count <= 10) - { - blTree.Frequencies[Repeat3To10]++; - } - else - { - blTree.Frequencies[Repeat11To138]++; - } - } - } - - /// - /// Write tree values - /// - /// Tree to write - public void WriteTree(Tree blTree) - { - int max_count; // max repeat count - int min_count; // min repeat count - int count; // repeat count of the current code - int curlen = -1; // length of current code - - int i = 0; - while (i < this.NumberOfCodes) - { - count = 1; - int nextlen = this.Length[i]; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else - { - max_count = 6; - min_count = 3; - if (curlen != nextlen) - { - blTree.WriteSymbol(nextlen); - count = 0; - } - } - - curlen = nextlen; - i++; - - while (i < this.NumberOfCodes && curlen == this.Length[i]) - { - i++; - if (++count >= max_count) - { - break; - } - } - - if (count < min_count) - { - while (count-- > 0) - { - blTree.WriteSymbol(curlen); - } - } - else if (curlen != 0) - { - blTree.WriteSymbol(Repeat3To6); - this.deflater.pending.WriteBits(count - 3, 2); - } - else if (count <= 10) - { - blTree.WriteSymbol(Repeat3To10); - this.deflater.pending.WriteBits(count - 3, 3); - } - else - { - blTree.WriteSymbol(Repeat11To138); - this.deflater.pending.WriteBits(count - 11, 7); - } - } - } - - private void BuildLength(int[] childs) - { - 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 < this.maxLength; i++) - { - this.blCounts[i] = 0; - } - - // First calculate optimal bit lengths - int[] lengths = new int[numNodes]; - lengths[numNodes - 1] = 0; - - for (int i = numNodes - 1; i >= 0; i--) - { - if (childs[(2 * i) + 1] != -1) - { - int bitLength = lengths[i] + 1; - if (bitLength > this.maxLength) - { - bitLength = this.maxLength; - overflow++; - } - - lengths[childs[2 * i]] = lengths[childs[(2 * i) + 1]] = bitLength; - } - else - { - // A leaf node - int bitLength = lengths[i]; - this.blCounts[bitLength - 1]++; - this.Length[childs[2 * i]] = (byte)lengths[i]; - } - } - - if (overflow == 0) - { - return; - } - - int incrBitLen = this.maxLength - 1; - do - { - // Find the first bit length which could increase: - while (this.blCounts[--incrBitLen] == 0) - { - } - - // Move this node one down and remove a corresponding - // number of overflow nodes. - do - { - 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 = this.maxLength; bits != 0; bits--) - { - int n = this.blCounts[bits - 1]; - while (n > 0) - { - int childPtr = 2 * childs[nodePtr++]; - if (childs[childPtr + 1] == -1) - { - // We found another leaf - this.Length[childs[childPtr]] = (byte)bits; - n--; - } - } - } - } - } - - /// - /// Pending buffer to use - /// - public DeflaterPending pending; - - Tree literalTree; - Tree distTree; - Tree blTree; - - // Buffer for distances - short[] d_buf; - byte[] l_buf; - int last_lit; - int extra_bits; - - static DeflaterHuffman() - { - // See RFC 1951 3.2.6 - // Literal codes - staticLCodes = new short[LiteralCount]; - staticLLength = new byte[LiteralCount]; - - int i = 0; - while (i < 144) - { - staticLCodes[i] = BitReverse((0x030 + i) << 8); - staticLLength[i++] = 8; - } - - while (i < 256) - { - staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); - staticLLength[i++] = 9; - } - - while (i < 280) - { - staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); - staticLLength[i++] = 7; - } - - while (i < LiteralCount) - { - staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); - staticLLength[i++] = 8; - } - - // Distance codes - staticDCodes = new short[DistanceCodeCount]; - staticDLength = new byte[DistanceCodeCount]; - for (i = 0; i < DistanceCodeCount; i++) - { - staticDCodes[i] = BitReverse(i << 11); - staticDLength[i] = 5; - } - } - - /// - /// Initializes a new instance of the class with a pending buffer. - /// - /// Pending buffer to use - public DeflaterHuffman(DeflaterPending pending) - { - this.pending = pending; - - this.literalTree = new Tree(this, LiteralCount, 257, 15); - this.distTree = new Tree(this, DistanceCodeCount, 1, 15); - this.blTree = new Tree(this, BitLengthCount, 4, 7); - - this.d_buf = new short[Buffersize]; - this.l_buf = new byte[Buffersize]; - } - - /// - /// 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() - { - this.last_lit = 0; - this.extra_bits = 0; - this.literalTree.Reset(); - this.distTree.Reset(); - this.blTree.Reset(); - } - - /// - /// Write all trees to pending buffer - /// - /// The number/rank of treecodes to send. - public void SendAllTrees(int blTreeCodes) - { - 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++) - { - this.pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3); - } - - this.literalTree.WriteTree(this.blTree); - this.distTree.WriteTree(this.blTree); - } - - /// - /// Compress current buffer writing data to pending buffer - /// - public void CompressBlock() - { - for (int i = 0; i < this.last_lit; i++) - { - int litlen = this.l_buf[i] & 0xff; - int dist = this.d_buf[i]; - if (dist-- != 0) - { - int lc = Lcode(litlen); - this.literalTree.WriteSymbol(lc); - - int bits = (lc - 261) / 4; - if (bits > 0 && bits <= 5) - { - this.pending.WriteBits(litlen & ((1 << bits) - 1), bits); - } - - int dc = Dcode(dist); - this.distTree.WriteSymbol(dc); - - bits = (dc / 2) - 1; - if (bits > 0) - { - this.pending.WriteBits(dist & ((1 << bits) - 1), bits); - } - } - else - { - this.literalTree.WriteSymbol(litlen); - } - } - - this.literalTree.WriteSymbol(Eof); - } - - /// - /// Flush block to output with no compression - /// - /// Data to write - /// Index of first byte to write - /// Count of bytes to write - /// True if this is the last block - public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { - 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(); - } - - /// - /// Flush block to output with compression - /// - /// Data to flush - /// Index of first byte to flush - /// Count of bytes to flush - /// True if this is the last block - public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { - this.literalTree.Frequencies[Eof]++; - - // Build trees - this.literalTree.BuildTree(); - this.distTree.BuildTree(); - - // Calculate bitlen frequency - this.literalTree.CalcBLFreq(this.blTree); - this.distTree.CalcBLFreq(this.blTree); - - // Build bitlen tree - this.blTree.BuildTree(); - - int blTreeCodes = 4; - for (int i = 18; i > blTreeCodes; i--) - { - if (this.blTree.Length[BitLengthOrder[i]] > 0) - { - blTreeCodes = i + 1; - } - } - - 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 += this.literalTree.Frequencies[i] * staticLLength[i]; - } - - for (int i = 0; i < DistanceCodeCount; i++) - { - static_len += this.distTree.Frequencies[i] * staticDLength[i]; - } - - if (opt_len >= static_len) - { - // Force static trees - opt_len = static_len; - } - - if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) - { - // Store Block - this.FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); - } - else if (opt_len == static_len) - { - // Encode with static tree - 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 - this.pending.WriteBits((DeflaterConstants.DynTrees << 1) + (lastBlock ? 1 : 0), 3); - this.SendAllTrees(blTreeCodes); - this.CompressBlock(); - this.Reset(); - } - } - - /// - /// Get value indicating if internal buffer is full - /// - /// true if buffer is full - public bool IsFull() - { - return this.last_lit >= Buffersize; - } - - /// - /// Add literal to buffer - /// - /// Literal value to add to buffer. - /// Value indicating internal buffer is full - public bool TallyLit(int literal) - { - this.d_buf[this.last_lit] = 0; - this.l_buf[this.last_lit++] = (byte)literal; - this.literalTree.Frequencies[literal]++; - return this.IsFull(); - } - - /// - /// Add distance code and length to literal and distance trees - /// - /// Distance code - /// Length - /// Value indicating if internal buffer is full - public bool TallyDist(int distance, int length) - { - this.d_buf[this.last_lit] = (short)distance; - this.l_buf[this.last_lit++] = (byte)(length - 3); - - int lc = Lcode(length - 3); - this.literalTree.Frequencies[lc]++; - if (lc >= 265 && lc < 285) - { - this.extra_bits += (lc - 261) / 4; - } - - int dc = Dcode(distance - 1); - this.distTree.Frequencies[dc]++; - if (dc >= 4) - { - this.extra_bits += (dc / 2) - 1; - } - - return this.IsFull(); - } - - private static int Lcode(int length) - { - if (length == 255) - { - return 285; - } - - int code = 257; - while (length >= 8) - { - code += 4; - length >>= 1; - } - - return code + length; - } - - private static int Dcode(int distance) - { - int code = 0; - while (distance >= 4) - { - code += 2; - distance >>= 1; - } - - return code + distance; - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs deleted file mode 100644 index fad471025..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterOutputStream.cs +++ /dev/null @@ -1,446 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System; - using System.IO; - - /// - /// A special stream deflating or compressing the bytes that are - /// written to it. It uses a Deflater to perform actual deflating.
- /// Authors of the original java version : Tom Tromey, Jochen Hoenicke - ///
- public class DeflaterOutputStream : Stream - { - /// - /// 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. - /// - public DeflaterOutputStream(Stream baseOutputStream) - : this(baseOutputStream, new Deflater(), 512) - { - } - - /// - /// Initializes a new instance of the class - /// with the given Deflater and default buffer size. - /// - /// - /// the output stream where deflated output should be written. - /// - /// - /// the underlying deflater. - /// - public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) - : this(baseOutputStream, deflater, 512) - { - } - - /// - /// Initializes a new instance of the class - /// with the given Deflater and buffer size. - /// - /// - /// The output stream where deflated output is written. - /// - /// - /// The underlying deflater to use - /// - /// - /// The buffer size in bytes to use when deflating (minimum value 512) - /// - /// 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(nameof(baseOutputStream)); - } - - if (baseOutputStream.CanWrite == false) - { - throw new ArgumentException("Must support writing", nameof(baseOutputStream)); - } - - if (deflater == null) - { - throw new ArgumentNullException(nameof(deflater)); - } - - if (bufferSize < 512) - { - throw new ArgumentOutOfRangeException(nameof(bufferSize)); - } - - this.baseOutputStream = baseOutputStream; - this.bytebuffer = new byte[bufferSize]; - this.deflater = deflater; - } - - /// - /// Get/set flag indicating ownership of the underlying stream. - /// When the flag is true will close the underlying stream also. - /// - public bool IsStreamOwner - { - get { return this.isStreamOwner; } - set { this.isStreamOwner = value; } - } - - /// - /// Allows client to determine if an entry can be patched after its added - /// - public bool CanPatchEntries => this.baseOutputStream.CanSeek; - - /// - /// Get/set the password used for encryption. - /// - /// When set to null or if the password is empty no encryption is performed - public string Password - { - get - { - return this.password; - } - - set - { - if ((value != null) && (value.Length == 0)) - { - this.password = null; - } - else - { - this.password = value; - } - } - } - - /// - /// Gets value indicating stream can be read from - /// - 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 => false; - - /// - /// Get value indicating if this stream supports writing - /// - public override bool CanWrite => this.baseOutputStream.CanWrite; - - /// - /// Get current length of stream - /// - public override long Length => this.baseOutputStream.Length; - - /// - /// Gets the current position within the stream. - /// - /// Any attempt to set position - public override long Position - { - get - { - 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! - /// - /// The offset relative to the to seek. - /// The to seek from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("DeflaterOutputStream Seek not supported"); - } - - /// - /// Sets the length of this stream to the given value. Not supported by this class! - /// - /// The new stream length. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); - } - - /// - /// Read a byte from stream advancing position by one - /// - /// The byte read cast to an int. THe value is -1 if at the end of the stream. - /// Any access - public override int ReadByte() - { - throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); - } - - /// - /// Read a block of bytes from stream - /// - /// The buffer to store read data in. - /// The offset to start storing at. - /// The maximum number of bytes to read. - /// The actual number of bytes read. Zero if end of stream is detected. - /// Any access - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("DeflaterOutputStream Read not supported"); - } - - /// - /// 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() - { - this.deflater.Flush(); - this.Deflate(); - this.baseOutputStream.Flush(); - } - - /// - /// Writes a single byte to the compressed output stream. - /// - /// - /// The byte value. - /// - public override void WriteByte(byte value) - { - byte[] b = new byte[1]; - b[0] = value; - this.Write(b, 0, 1); - } - - /// - /// Writes bytes from an array to the compressed stream. - /// - /// - /// The byte array - /// - /// - /// The offset into the byte array where to start. - /// - /// - /// The number of bytes to write. - /// - public override void Write(byte[] buffer, int offset, int count) - { - this.deflater.SetInput(buffer, offset, count); - this.Deflate(); - } - - /// - /// 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 . - /// - /// 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); - } - } - - /// - /// Encrypt a single byte - /// - /// - /// The encrypted value - /// - protected byte EncryptByte() - { - uint temp = (this.keys[2] & 0xFFFF) | 2; - return (byte)((temp * (temp ^ 1)) >> 8); - } - - /// - /// Update encryption keys - /// - /// 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)); - } - - /// - /// 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); - - if (deflateCount <= 0) - { - break; - } - - if (this.keys != null) - { - this.EncryptBlock(this.bytebuffer, 0, deflateCount); - } - - 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; - - 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 deleted file mode 100644 index 67912bc30..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/DeflaterPending.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) James Jackson-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 - { - /// - /// Initializes a new instance of the class. - /// Construct instance with default buffer size - /// - public DeflaterPending() - : base(DeflaterConstants.PendingBufSize) - { - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs b/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs deleted file mode 100644 index d47bde407..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/GeneralBitFlags.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System; - - /// - /// Defines the contents of the general bit flags field for an archive entry. - /// - [Flags] - public enum GeneralBitFlags - { - /// - /// Bit 0 if set indicates that the file is encrypted - /// - Encrypted = 0x0001, - - /// - /// Bits 1 and 2 - Two bits defining the compression method (only for Method 6 Imploding and 8,9 Deflating) - /// - Method = 0x0006, - - /// - /// Bit 3 if set indicates a trailing data desciptor is appended to the entry data - /// - Descriptor = 0x0008, - - /// - /// Bit 4 is reserved for use with method 8 for enhanced deflation - /// - ReservedPKware4 = 0x0010, - - /// - /// Bit 5 if set indicates the file contains Pkzip compressed patched data. - /// Requires version 2.7 or greater. - /// - Patched = 0x0020, - - /// - /// Bit 6 if set indicates strong encryption has been used for this entry. - /// - StrongEncryption = 0x0040, - - /// - /// Bit 7 is currently unused - /// - Unused7 = 0x0080, - - /// - /// Bit 8 is currently unused - /// - Unused8 = 0x0100, - - /// - /// Bit 9 is currently unused - /// - Unused9 = 0x0200, - - /// - /// Bit 10 is currently unused - /// - Unused10 = 0x0400, - - /// - /// Bit 11 if set indicates the filename and - /// comment fields for this file must be encoded using UTF-8. - /// - UnicodeText = 0x0800, - - /// - /// Bit 12 is documented as being reserved by PKware for enhanced compression. - /// - EnhancedCompress = 0x1000, - - /// - /// Bit 13 if set indicates that values in the local header are masked to hide - /// their actual values, and the central directory is encrypted. - /// - /// - /// Used when encrypting the central directory contents. - /// - HeaderMasked = 0x2000, - - /// - /// Bit 14 is documented as being reserved for use by PKware - /// - ReservedPkware14 = 0x4000, - - /// - /// Bit 15 is documented as being reserved for use by PKware - /// - ReservedPkware15 = 0x8000 - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs b/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs index 7e6d65be1..d3ef70ed4 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs @@ -9,7 +9,7 @@ namespace ImageProcessor.Formats /// Interface to compute a data checksum used by checked input/output streams. /// A data checksum can be updated by one byte or with a byte array. After each /// update the value of the current checksum can be returned by calling - /// getValue. The complete checksum object can also be reset + /// Value. The complete checksum object can also be reset /// so it can be used again with new data. ///
public interface IChecksum diff --git a/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs b/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs deleted file mode 100644 index 35a96741d..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/Inflater.cs +++ /dev/null @@ -1,843 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - /// - /// Inflater is used to decompress data that has been compressed according - /// to the "deflate" standard described in rfc1951. - /// - /// By default Zlib (rfc1950) headers and footers are expected in the input. - /// You can use constructor public Inflater(bool noHeader) passing true - /// if there is no Zlib header information - /// - /// The usage is as following. First you have to set some input with - /// SetInput(), then Inflate() it. If inflate doesn't - /// inflate any bytes there may be three reasons: - /// - /// Once the first output byte is produced, a dictionary will not be - /// needed at a later stage. - /// - /// author of the original java version : John Leuner, Jochen Hoenicke - /// - public class Inflater - { - /// - /// Copy lengths for literal codes 257..285 - /// - private static 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 - /// - 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 - }; - - /// - /// Copy offsets for distance codes 0..29 - /// - 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 - }; - - /// - /// Extra bits for distance codes - /// - 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 - }; - - /// - /// These are the possible states for an inflater - /// - private const int DECODE_HEADER = 0; - private const int DECODE_DICT = 1; - private const int DECODE_BLOCKS = 2; - private const int DECODE_STORED_LEN1 = 3; - private const int DECODE_STORED_LEN2 = 4; - private const int DECODE_STORED = 5; - private const int DECODE_DYN_HEADER = 6; - private const int DECODE_HUFFMAN = 7; - private const int DECODE_HUFFMAN_LENBITS = 8; - private const int DECODE_HUFFMAN_DIST = 9; - private const int DECODE_HUFFMAN_DISTBITS = 10; - private const int DECODE_CHKSUM = 11; - private const int FINISHED = 12; - - /// - /// This variable contains the current state. - /// - private int mode; - - /// - /// The adler checksum of the dictionary or of the decompressed - /// stream, as it is written in the header resp. footer of the - /// compressed stream. - /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. - /// - private int readAdler; - - /// - /// The number of bits needed to complete the current state. This - /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, - /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. - /// - private int neededBits; - private int repLength; - 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. - /// - private bool isLastBlock; - - /// - /// The total number of inflated bytes. - /// - 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. - /// - 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 - /// footer. - /// - private bool noHeader; - - private StreamManipulator input; - private OutputWindow outputWindow; - private InflaterDynHeader dynHeader; - private InflaterHuffmanTree litlenTree, distTree; - private Adler32 adler; - - /// - /// Initializes a new instance of the class. - /// RFC1950/Zlib headers and footers will be expected in the input data - /// - public Inflater() - : this(false) - { - } - - /// - /// 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. - /// - public Inflater(bool noHeader) - { - this.noHeader = noHeader; - this.adler = new Adler32(); - this.input = new StreamManipulator(); - this.outputWindow = new OutputWindow(); - this.mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; - } - - /// - /// Resets the inflater so that a new stream can be decompressed. All - /// pending input and output will be discarded. - /// - public void Reset() - { - 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(); - } - - /// - /// Decodes a zlib/RFC1950 header. - /// - /// - /// False if more input is needed. - /// - /// - /// The header is invalid. - /// - private bool DecodeHeader() - { - int header = this.input.PeekBits(16); - if (header < 0) - { - return false; - } - - this.input.DropBits(16); - - // The header is written in "wrong" byte order - header = ((header << 8) | (header >> 8)) & 0xffff; - if (header % 31 != 0) - { - throw new ImageFormatException("Header checksum illegal"); - } - - if ((header & 0x0f00) != (Deflater.Deflated << 8)) - { - throw new ImageFormatException("Compression Method unknown"); - } - - /* Maximum size of the backwards window in bits. - * We currently ignore this, but we could use it to make the - * inflater window more space efficient. On the other hand the - * full window (15 bits) is needed most times, anyway. - int max_wbits = ((header & 0x7000) >> 12) + 8; - */ - - if ((header & 0x0020) == 0) - { // Dictionary flag? - this.mode = DECODE_BLOCKS; - } - else - { - this.mode = DECODE_DICT; - this.neededBits = 32; - } - - return true; - } - - /// - /// Decodes the dictionary checksum after the deflate header. - /// - /// - /// False if more input is needed. - /// - private bool DecodeDict() - { - while (this.neededBits > 0) - { - int dictByte = this.input.PeekBits(8); - if (dictByte < 0) - { - return false; - } - - this.input.DropBits(8); - this.readAdler = (this.readAdler << 8) | dictByte; - this.neededBits -= 8; - } - - return false; - } - - /// - /// Decodes the huffman encoded symbols in the input stream. - /// - /// - /// false if more input is needed, true if output window is - /// full or the current block ends. - /// - /// - /// if deflated stream is invalid. - /// - private bool DecodeHuffman() - { - int free = this.outputWindow.GetFreeSpace(); - while (free >= 258) - { - int symbol; - switch (this.mode) - { - case DECODE_HUFFMAN: - // This is the inner loop so it is optimized a bit - while (((symbol = this.litlenTree.GetSymbol(this.input)) & ~0xff) == 0) - { - this.outputWindow.Write(symbol); - if (--free < 258) - { - return true; - } - } - - if (symbol < 257) - { - if (symbol < 0) - { - return false; - } - else - { - // symbol == 256: end of block - this.distTree = null; - this.litlenTree = null; - this.mode = DECODE_BLOCKS; - return true; - } - } - - try - { - 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 (this.neededBits > 0) - { - this.mode = DECODE_HUFFMAN_LENBITS; - int i = this.input.PeekBits(this.neededBits); - if (i < 0) - { - return false; - } - - this.input.DropBits(this.neededBits); - this.repLength += i; - } - - this.mode = DECODE_HUFFMAN_DIST; - goto case DECODE_HUFFMAN_DIST; // fall through - - case DECODE_HUFFMAN_DIST: - symbol = this.distTree.GetSymbol(this.input); - if (symbol < 0) - { - return false; - } - - try - { - this.repDist = CPDIST[symbol]; - this.neededBits = CPDEXT[symbol]; - } - catch (Exception) - { - throw new ImageFormatException("Illegal rep dist code"); - } - - goto case DECODE_HUFFMAN_DISTBITS; // fall through - - case DECODE_HUFFMAN_DISTBITS: - if (this.neededBits > 0) - { - this.mode = DECODE_HUFFMAN_DISTBITS; - int i = this.input.PeekBits(this.neededBits); - if (i < 0) - { - return false; - } - - this.input.DropBits(this.neededBits); - this.repDist += i; - } - - this.outputWindow.Repeat(this.repLength, this.repDist); - free -= this.repLength; - this.mode = DECODE_HUFFMAN; - break; - - default: - throw new ImageFormatException("Inflater unknown mode"); - } - } - - return true; - } - - /// - /// Decodes the adler checksum after the deflate stream. - /// - /// - /// false if more input is needed. - /// - /// - /// If checksum doesn't match. - /// - private bool DecodeChksum() - { - while (this.neededBits > 0) - { - int chkByte = this.input.PeekBits(8); - if (chkByte < 0) - { - return false; - } - - this.input.DropBits(8); - this.readAdler = (this.readAdler << 8) | chkByte; - this.neededBits -= 8; - } - - if ((int)this.adler.Value != this.readAdler) - { - throw new ImageFormatException("Adler chksum doesn't match: " + (int)this.adler.Value + " vs. " + this.readAdler); - } - - this.mode = FINISHED; - return false; - } - - /// - /// Decodes the deflated stream. - /// - /// - /// false if more input is needed, or if finished. - /// - /// - /// if deflated stream is invalid. - /// - private bool Decode() - { - switch (this.mode) - { - case DECODE_HEADER: - return this.DecodeHeader(); - - case DECODE_DICT: - return this.DecodeDict(); - - case DECODE_CHKSUM: - return this.DecodeChksum(); - - case DECODE_BLOCKS: - if (this.isLastBlock) - { - if (this.noHeader) - { - this.mode = FINISHED; - return false; - } - else - { - this.input.SkipToByteBoundary(); - this.neededBits = 32; - this.mode = DECODE_CHKSUM; - return true; - } - } - - int type = this.input.PeekBits(3); - if (type < 0) - { - return false; - } - - this.input.DropBits(3); - - if ((type & 1) != 0) - { - this.isLastBlock = true; - } - - switch (type >> 1) - { - case DeflaterConstants.StoredBlock: - this.input.SkipToByteBoundary(); - this.mode = DECODE_STORED_LEN1; - break; - case DeflaterConstants.StaticTrees: - this.litlenTree = InflaterHuffmanTree.defLitLenTree; - this.distTree = InflaterHuffmanTree.defDistTree; - this.mode = DECODE_HUFFMAN; - break; - 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 ((this.uncomprLen = this.input.PeekBits(16)) < 0) - { - return false; - } - - this.input.DropBits(16); - this.mode = DECODE_STORED_LEN2; - } - - goto case DECODE_STORED_LEN2; // fall through - - case DECODE_STORED_LEN2: - { - int nlen = this.input.PeekBits(16); - if (nlen < 0) - { - return false; - } - - this.input.DropBits(16); - if (nlen != (this.uncomprLen ^ 0xffff)) - { - throw new ImageFormatException("broken uncompressed block"); - } - - this.mode = DECODE_STORED; - } - - goto case DECODE_STORED; // fall through - - case DECODE_STORED: - { - int more = this.outputWindow.CopyStored(this.input, this.uncomprLen); - this.uncomprLen -= more; - if (this.uncomprLen == 0) - { - this.mode = DECODE_BLOCKS; - return true; - } - - return !this.input.IsNeedingInput; - } - - case DECODE_DYN_HEADER: - if (!this.dynHeader.Decode(this.input)) - { - return false; - } - - 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 this.DecodeHuffman(); - - case FINISHED: - return false; - - default: - throw new ImageFormatException("Inflater.Decode unknown mode"); - } - } - - /// - /// Sets the preset dictionary. This should only be called, if - /// needsDictionary() returns true and it should set the same - /// dictionary, that was used for deflating. The getAdler() - /// function returns the checksum of the dictionary needed. - /// - /// - /// The dictionary. - /// - public void SetDictionary(byte[] buffer) - { - this.SetDictionary(buffer, 0, buffer.Length); - } - - /// - /// Sets the preset dictionary. This should only be called, if - /// needsDictionary() returns true and it should set the same - /// dictionary, that was used for deflating. The getAdler() - /// function returns the checksum of the dictionary needed. - /// - /// - /// The dictionary. - /// - /// - /// The index into buffer where the dictionary starts. - /// - /// - /// The number of bytes in the dictionary. - /// - /// - /// No dictionary is needed. - /// - /// - /// The adler checksum for the buffer is invalid - /// - public void SetDictionary(byte[] buffer, int index, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (!this.IsNeedingDictionary) - { - throw new InvalidOperationException("Dictionary is not needed"); - } - - this.adler.Update(buffer, index, count); - - if ((int)this.adler.Value != this.readAdler) - { - throw new ImageFormatException("Wrong adler checksum"); - } - - this.adler.Reset(); - this.outputWindow.CopyDict(buffer, index, count); - this.mode = DECODE_BLOCKS; - } - - /// - /// Sets the input. This should only be called, if needsInput() - /// returns true. - /// - /// - /// the input. - /// - public void SetInput(byte[] buffer) - { - this.SetInput(buffer, 0, buffer.Length); - } - - /// - /// Sets the input. This should only be called, if needsInput() - /// returns true. - /// - /// - /// The source of input data - /// - /// - /// The index into buffer where the input starts. - /// - /// - /// The number of bytes of input to use. - /// - /// - /// No input is needed. - /// - /// - /// The index and/or count are wrong. - /// - public void SetInput(byte[] buffer, int index, int count) - { - this.input.SetInput(buffer, index, count); - this.totalIn += (long)count; - } - - /// - /// Inflates the compressed stream to the output buffer. If this - /// returns 0, you should check, whether IsNeedingDictionary(), - /// IsNeedingInput() or IsFinished() returns true, to determine why no - /// further output is produced. - /// - /// - /// the output buffer. - /// - /// - /// The number of bytes written to the buffer, 0 if no further - /// output can be produced. - /// - /// - /// if buffer has length 0. - /// - /// - /// if deflated stream is invalid. - /// - public int Inflate(byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - return this.Inflate(buffer, 0, buffer.Length); - } - - /// - /// Inflates the compressed stream to the output buffer. If this - /// returns 0, you should check, whether needsDictionary(), - /// needsInput() or finished() returns true, to determine why no - /// further output is produced. - /// - /// - /// the output buffer. - /// - /// - /// the offset in buffer where storing starts. - /// - /// - /// the maximum number of bytes to output. - /// - /// - /// the number of bytes written to the buffer, 0 if no further output can be produced. - /// - /// - /// if count is less than 0. - /// - /// - /// if the index and / or count are wrong. - /// - /// - /// if deflated stream is invalid. - /// - public int Inflate(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "count cannot be negative"); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be negative"); - } - - if (offset + count > buffer.Length) - { - throw new ArgumentException("count exceeds buffer bounds"); - } - - // Special case: count may be zero - if (count == 0) - { - if (!this.IsFinished) - { // -jr- 08-Nov-2003 INFLATE_BUG fix.. - this.Decode(); - } - - return 0; - } - - int bytesCopied = 0; - - do - { - 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 = this.outputWindow.CopyOutput(buffer, offset, count); - if (more > 0) - { - this.adler.Update(buffer, offset, more); - offset += more; - bytesCopied += more; - this.totalOut += (long)more; - count -= more; - if (count == 0) - { - return bytesCopied; - } - } - } - } - 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(). - /// NOTE: This method also returns true when the stream is finished. - /// - public bool IsNeedingInput => this.input.IsNeedingInput; - - /// - /// Returns true, if a preset dictionary is needed to inflate the input. - /// - 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 => this.mode == FINISHED && this.outputWindow.GetAvailable() == 0; - - /// - /// Gets the adler checksum. This is either the checksum of all - /// uncompressed bytes returned by inflate(), or if needsDictionary() - /// returns true (and thus no output was yet produced) this is the - /// adler checksum of the expected dictionary. - /// - /// - /// the adler checksum. - /// - public int Adler => this.IsNeedingDictionary ? this.readAdler : (int)this.adler.Value; - - /// - /// Gets the total number of output bytes returned by Inflate(). - /// - /// - /// the total number of output bytes. - /// - public long TotalOut => this.totalOut; - - /// - /// Gets the total number of processed compressed input bytes. - /// - /// - /// The total number of bytes of processed input bytes. - /// - public long TotalIn => this.totalIn - (long)this.RemainingInput; - - /// - /// Gets the number of unprocessed input bytes. Useful, if the end of the - /// stream is reached and you want to further process the bytes after - /// the deflate stream. - /// - /// - /// The number of bytes of the input which have not been processed. - /// - public int RemainingInput => this.input.AvailableBytes; // TODO: Should this be a long? - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs deleted file mode 100644 index d1811dd3b..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterDynHeader.cs +++ /dev/null @@ -1,196 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - class InflaterDynHeader - { - #region Constants - const int LNUM = 0; - const int DNUM = 1; - const int BLNUM = 2; - const int BLLENS = 3; - const int LENS = 4; - const int REPS = 5; - - static readonly int[] repMin = { 3, 3, 11 }; - static readonly int[] repBits = { 2, 3, 7 }; - - static readonly int[] BL_ORDER = - { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - #endregion - - #region Constructors - public InflaterDynHeader() - { - } - #endregion - - public bool Decode(StreamManipulator input) - { - decode_loop: - for (;;) - { - switch (mode) - { - case LNUM: - lnum = input.PeekBits(5); - if (lnum < 0) - { - return false; - } - lnum += 257; - input.DropBits(5); - // System.err.println("LNUM: "+lnum); - mode = DNUM; - goto case DNUM; // fall through - case DNUM: - dnum = input.PeekBits(5); - if (dnum < 0) - { - return false; - } - dnum++; - input.DropBits(5); - // System.err.println("DNUM: "+dnum); - num = lnum + dnum; - litdistLens = new byte[num]; - mode = BLNUM; - goto case BLNUM; // fall through - case BLNUM: - blnum = input.PeekBits(4); - if (blnum < 0) - { - return false; - } - blnum += 4; - input.DropBits(4); - blLens = new byte[19]; - ptr = 0; - // System.err.println("BLNUM: "+blnum); - mode = BLLENS; - goto case BLLENS; // fall through - case BLLENS: - while (ptr < blnum) - { - int len = input.PeekBits(3); - if (len < 0) - { - return false; - } - input.DropBits(3); - // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); - blLens[BL_ORDER[ptr]] = (byte)len; - ptr++; - } - blTree = new InflaterHuffmanTree(blLens); - blLens = null; - ptr = 0; - mode = LENS; - goto case LENS; // fall through - case LENS: - { - int symbol; - while (((symbol = blTree.GetSymbol(input)) & ~15) == 0) - { - /* Normal case: symbol in [0..15] */ - - // System.err.println("litdistLens["+ptr+"]: "+symbol); - litdistLens[ptr++] = lastLen = (byte)symbol; - - if (ptr == num) - { - /* Finished */ - return true; - } - } - - /* need more input ? */ - if (symbol < 0) - { - return false; - } - - /* otherwise repeat code */ - if (symbol >= 17) - { - /* repeat zero */ - // System.err.println("repeating zero"); - lastLen = 0; - } - else - { - if (ptr == 0) - { - throw new ImageFormatException(); - } - } - repSymbol = symbol - 16; - } - mode = REPS; - goto case REPS; // fall through - case REPS: - { - int bits = repBits[repSymbol]; - int count = input.PeekBits(bits); - if (count < 0) - { - return false; - } - input.DropBits(bits); - count += repMin[repSymbol]; - // System.err.println("litdistLens repeated: "+count); - - if (ptr + count > num) - { - throw new ImageFormatException(); - } - while (count-- > 0) - { - litdistLens[ptr++] = lastLen; - } - - if (ptr == num) - { - /* Finished */ - return true; - } - } - mode = LENS; - goto decode_loop; - } - } - } - - public InflaterHuffmanTree BuildLitLenTree() - { - byte[] litlenLens = new byte[lnum]; - Array.Copy(litdistLens, 0, litlenLens, 0, lnum); - return new InflaterHuffmanTree(litlenLens); - } - - public InflaterHuffmanTree BuildDistTree() - { - byte[] distLens = new byte[dnum]; - Array.Copy(litdistLens, lnum, distLens, 0, dnum); - return new InflaterHuffmanTree(distLens); - } - - #region Instance Fields - byte[] blLens; - byte[] litdistLens; - - InflaterHuffmanTree blTree; - - /// - /// The current decode mode - /// - int mode; - int lnum, dnum, blnum, num; - int repSymbol; - byte lastLen; - int ptr; - #endregion - - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs deleted file mode 100644 index f6b7332f5..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterHuffmanTree.cs +++ /dev/null @@ -1,225 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - /// - /// Huffman tree used for inflation - /// - public class InflaterHuffmanTree - { - #region Constants - const int MAX_BITLEN = 15; - #endregion - - #region Instance Fields - short[] tree; - #endregion - - /// - /// Literal length tree - /// - public static InflaterHuffmanTree defLitLenTree; - - /// - /// Distance tree - /// - public static InflaterHuffmanTree defDistTree; - - static InflaterHuffmanTree() - { - try - { - byte[] codeLengths = new byte[288]; - int i = 0; - while (i < 144) - { - codeLengths[i++] = 8; - } - while (i < 256) - { - codeLengths[i++] = 9; - } - while (i < 280) - { - codeLengths[i++] = 7; - } - while (i < 288) - { - codeLengths[i++] = 8; - } - defLitLenTree = new InflaterHuffmanTree(codeLengths); - - codeLengths = new byte[32]; - i = 0; - while (i < 32) - { - codeLengths[i++] = 5; - } - defDistTree = new InflaterHuffmanTree(codeLengths); - } - catch (Exception) - { - throw new ImageFormatException("InflaterHuffmanTree: static tree length illegal"); - } - } - - #region Constructors - /// - /// Constructs a Huffman tree from the array of code lengths. - /// - /// - /// the array of code lengths - /// - public InflaterHuffmanTree(byte[] codeLengths) - { - BuildTree(codeLengths); - } - #endregion - - void BuildTree(byte[] codeLengths) - { - int[] blCount = new int[MAX_BITLEN + 1]; - int[] nextCode = new int[MAX_BITLEN + 1]; - - for (int i = 0; i < codeLengths.Length; i++) - { - int bits = codeLengths[i]; - if (bits > 0) - { - blCount[bits]++; - } - } - - int code = 0; - int treeSize = 512; - for (int bits = 1; bits <= MAX_BITLEN; bits++) - { - nextCode[bits] = code; - code += blCount[bits] << (16 - bits); - if (bits >= 10) - { - /* We need an extra table for bit lengths >= 10. */ - int start = nextCode[bits] & 0x1ff80; - int end = code & 0x1ff80; - treeSize += (end - start) >> (16 - bits); - } - } - - /* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g - if (code != 65536) - { - throw new SharpZipBaseException("Code lengths don't add up properly."); - } - */ - /* Now create and fill the extra tables from longest to shortest - * bit len. This way the sub trees will be aligned. - */ - tree = new short[treeSize]; - int treePtr = 512; - for (int bits = MAX_BITLEN; bits >= 10; bits--) - { - int end = code & 0x1ff80; - code -= blCount[bits] << (16 - bits); - int start = code & 0x1ff80; - for (int i = start; i < end; i += 1 << 7) - { - tree[DeflaterHuffman.BitReverse(i)] = (short)((-treePtr << 4) | bits); - treePtr += 1 << (bits - 9); - } - } - - for (int i = 0; i < codeLengths.Length; i++) - { - int bits = codeLengths[i]; - if (bits == 0) - { - continue; - } - code = nextCode[bits]; - int revcode = DeflaterHuffman.BitReverse(code); - if (bits <= 9) - { - do - { - tree[revcode] = (short)((i << 4) | bits); - revcode += 1 << bits; - } while (revcode < 512); - } - else - { - int subTree = tree[revcode & 511]; - int treeLen = 1 << (subTree & 15); - subTree = -(subTree >> 4); - do - { - tree[subTree | (revcode >> 9)] = (short)((i << 4) | bits); - revcode += 1 << bits; - } while (revcode < treeLen); - } - nextCode[bits] = code + (1 << (16 - bits)); - } - - } - - /// - /// Reads the next symbol from input. The symbol is encoded using the - /// huffman tree. - /// - /// - /// input the input source. - /// - /// - /// the next symbol, or -1 if not enough input is available. - /// - public int GetSymbol(StreamManipulator input) - { - int lookahead, symbol; - if ((lookahead = input.PeekBits(9)) >= 0) - { - if ((symbol = tree[lookahead]) >= 0) - { - input.DropBits(symbol & 15); - return symbol >> 4; - } - int subtree = -(symbol >> 4); - int bitlen = symbol & 15; - if ((lookahead = input.PeekBits(bitlen)) >= 0) - { - symbol = tree[subtree | (lookahead >> 9)]; - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - int bits = input.AvailableBits; - lookahead = input.PeekBits(bits); - symbol = tree[subtree | (lookahead >> 9)]; - if ((symbol & 15) <= bits) - { - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - return -1; - } - } - } - else - { - int bits = input.AvailableBits; - lookahead = input.PeekBits(bits); - symbol = tree[lookahead]; - if (symbol >= 0 && (symbol & 15) <= bits) - { - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - return -1; - } - } - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs deleted file mode 100644 index 5c70ad41c..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs +++ /dev/null @@ -1,268 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - using System.IO; - - /// - /// An input buffer customised for use by - /// - /// - /// The buffer supports decryption of incoming data. - /// - public class InflaterInputBuffer - { - /// - /// Initialise a new instance of with a default buffer size - /// - /// The stream to buffer. - public InflaterInputBuffer(Stream stream) : this(stream, 4096) - { - } - - /// - /// Initialise a new instance of - /// - /// The stream to buffer. - /// The size to use for the buffer - /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. - public InflaterInputBuffer(Stream stream, int bufferSize) - { - inputStream = stream; - if (bufferSize < 1024) - { - bufferSize = 1024; - } - rawData = new byte[bufferSize]; - clearText = rawData; - } - - /// - /// Get the length of bytes bytes in the - /// - public int RawLength - { - get - { - return rawLength; - } - } - - /// - /// Get the contents of the raw data buffer. - /// - /// This may contain encrypted data. - public byte[] RawData - { - get - { - return rawData; - } - } - - /// - /// Get the number of useable bytes in - /// - public int ClearTextLength - { - get - { - return clearTextLength; - } - } - - /// - /// Get the contents of the clear text buffer. - /// - public byte[] ClearText - { - get - { - return clearText; - } - } - - /// - /// Get/set the number of bytes available - /// - public int Available - { - get { return available; } - set { available = value; } - } - - /// - /// Call passing the current clear text buffer contents. - /// - /// The inflater to set input for. - public void SetInflaterInput(Inflater inflater) - { - if (available > 0) - { - inflater.SetInput(clearText, clearTextLength - available, available); - available = 0; - } - } - - /// - /// Fill the buffer from the underlying input stream. - /// - public void Fill() - { - rawLength = 0; - int toRead = rawData.Length; - - while (toRead > 0) - { - int count = inputStream.Read(rawData, rawLength, toRead); - if (count <= 0) - { - break; - } - rawLength += count; - toRead -= count; - } - - clearTextLength = rawLength; - available = clearTextLength; - } - - /// - /// Read a buffer directly from the input stream - /// - /// The buffer to fill - /// Returns the number of bytes read. - public int ReadRawBuffer(byte[] buffer) - { - return ReadRawBuffer(buffer, 0, buffer.Length); - } - - /// - /// Read a buffer directly from the input stream - /// - /// The buffer to read into - /// The offset to start reading data into. - /// The number of bytes to read. - /// Returns the number of bytes read. - public int ReadRawBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException("length"); - } - - int currentOffset = offset; - int currentLength = length; - - while (currentLength > 0) - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - return 0; - } - } - - int toCopy = Math.Min(currentLength, available); - Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - available -= toCopy; - } - - return length; - } - - /// - /// Read clear text data from the input stream. - /// - /// The buffer to add data to. - /// The offset to start adding data at. - /// The number of bytes to read. - /// Returns the number of bytes actually read. - public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException("length"); - } - - int currentOffset = offset; - int currentLength = length; - - while (currentLength > 0) - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - return 0; - } - } - - int toCopy = Math.Min(currentLength, available); - Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - available -= toCopy; - } - return length; - } - - /// - /// Read a from the input stream. - /// - /// Returns the byte read. - public int ReadLeByte() - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - throw new ImageFormatException("EOF in header"); - } - } - byte result = rawData[rawLength - available]; - available -= 1; - return result; - } - - /// - /// Read an in little endian byte order. - /// - /// The short value read case to an int. - public int ReadLeShort() - { - return ReadLeByte() | (ReadLeByte() << 8); - } - - /// - /// Read an in little endian byte order. - /// - /// The int value read. - public int ReadLeInt() - { - return ReadLeShort() | (ReadLeShort() << 16); - } - - /// - /// Read a in little endian byte order. - /// - /// The long value read. - public long ReadLeLong() - { - return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); - } - - int rawLength; - byte[] rawData; - - int clearTextLength; - byte[] clearText; - int available; - Stream inputStream; - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs deleted file mode 100644 index b6a35e420..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs +++ /dev/null @@ -1,397 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - using System.IO; - - //using ICSharpCode.SharpZipLib; - //using ICSharpCode.SharpZipLib.Zip; - //using ICSharpCode.SharpZipLib.Zip.Compression; - - /// - /// This filter stream is used to decompress data compressed using the "deflate" - /// format. The "deflate" format is described in RFC 1951. - /// - /// This stream may form the basis for other decompression filters, such - /// as the GZipInputStream. - /// - /// Author of the original java version : John Leuner. - /// - public class InflaterInputStream : Stream - { - #region Constructors - /// - /// Create an InflaterInputStream with the default decompressor - /// and a default buffer size of 4KB. - /// - /// - /// The InputStream to read bytes from - /// - public InflaterInputStream(Stream baseInputStream) - : this(baseInputStream, new Inflater(), 4096) - { - } - - /// - /// Create an InflaterInputStream with the specified decompressor - /// and a default buffer size of 4KB. - /// - /// - /// The source of input data - /// - /// - /// The decompressor used to decompress data read from baseInputStream - /// - public InflaterInputStream(Stream baseInputStream, Inflater inf) - : this(baseInputStream, inf, 4096) - { - } - - /// - /// Create an InflaterInputStream with the specified decompressor and the specified buffer size. - /// - /// - /// The InputStream to read bytes from - /// - /// - /// The decompressor to use - /// - /// - /// Size of the buffer to use - /// - public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) - { - if (baseInputStream == null) - { - throw new ArgumentNullException("baseInputStream"); - } - - if (inflater == null) - { - throw new ArgumentNullException("inflater"); - } - - if (bufferSize <= 0) - { - throw new ArgumentOutOfRangeException("bufferSize"); - } - - this.baseInputStream = baseInputStream; - this.inf = inflater; - - inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); - } - - #endregion - - /// - /// Get/set flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// - /// The default value is true. - /// - public bool IsStreamOwner - { - get { return isStreamOwner; } - set { isStreamOwner = value; } - } - - /// - /// Skip specified number of bytes of uncompressed data - /// - /// - /// Number of bytes to skip - /// - /// - /// The number of bytes skipped, zero if the end of - /// stream has been reached - /// - /// - /// The number of bytes to skip is less than or equal to zero. - /// - public long Skip(long count) - { - if (count <= 0) - { - throw new ArgumentOutOfRangeException("count"); - } - - // v0.80 Skip by seeking if underlying stream supports it... - if (baseInputStream.CanSeek) - { - baseInputStream.Seek(count, SeekOrigin.Current); - return count; - } - else - { - int length = 2048; - if (count < length) - { - length = (int)count; - } - - byte[] tmp = new byte[length]; - int readCount = 1; - long toSkip = count; - - while ((toSkip > 0) && (readCount > 0)) - { - if (toSkip < length) - { - length = (int)toSkip; - } - - readCount = baseInputStream.Read(tmp, 0, length); - toSkip -= readCount; - } - - return count - toSkip; - } - } - - /// - /// Returns 0 once the end of the stream (EOF) has been reached. - /// Otherwise returns 1. - /// - public virtual int Available - { - get - { - return inf.IsFinished ? 0 : 1; - } - } - - /// - /// Fills the buffer with more data to decompress. - /// - /// - /// Stream ends early - /// - protected void Fill() - { - // Protect against redundant calls - if (inputBuffer.Available <= 0) - { - inputBuffer.Fill(); - if (inputBuffer.Available <= 0) - { - throw new ImageFormatException("Unexpected EOF"); - } - } - inputBuffer.SetInflaterInput(inf); - } - - #region Stream Overrides - /// - /// Gets a value indicating whether the current stream supports reading - /// - public override bool CanRead - { - get - { - return baseInputStream.CanRead; - } - } - - /// - /// Gets a value of false indicating seeking is not supported for this stream. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value of false indicating that this stream is not writeable. - /// - public override bool CanWrite - { - get - { - return false; - } - } - - /// - /// A value representing the length of the stream in bytes. - /// - public override long Length - { - get - { - return inputBuffer.RawLength; - } - } - - /// - /// The current position within the stream. - /// Throws a NotSupportedException when attempting to set the position - /// - /// Attempting to set the position - public override long Position - { - get - { - return baseInputStream.Position; - } - set - { - throw new NotSupportedException("InflaterInputStream Position not supported"); - } - } - - /// - /// Flushes the baseInputStream - /// - public override void Flush() - { - baseInputStream.Flush(); - } - - /// - /// Sets the position within the current stream - /// Always throws a NotSupportedException - /// - /// The relative offset to seek to. - /// The defining where to seek from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("Seek not supported"); - } - - /// - /// Set the length of the current stream - /// Always throws a NotSupportedException - /// - /// The new length value for the stream. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("InflaterInputStream SetLength not supported"); - } - - /// - /// Writes a sequence of bytes to stream and advances the current position - /// This method always throws a NotSupportedException - /// - /// Thew buffer containing data to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// Any access - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("InflaterInputStream Write not supported"); - } - - /// - /// Writes one byte to the current stream and advances the current position - /// Always throws a NotSupportedException - /// - /// The byte to write. - /// Any access - public override void WriteByte(byte value) - { - throw new NotSupportedException("InflaterInputStream WriteByte not supported"); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing && !isClosed) - { - isClosed = true; - if (isStreamOwner) - { - baseInputStream.Dispose(); - } - } - } - - /// - /// Reads decompressed data into the provided buffer byte array - /// - /// - /// The array to read and decompress data into - /// - /// - /// The offset indicating where the data should be placed - /// - /// - /// The number of bytes to decompress - /// - /// The number of bytes read. Zero signals the end of stream - /// - /// Inflater needs a dictionary - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (inf.IsNeedingDictionary) - { - throw new ImageFormatException("Need a dictionary"); - } - - int remainingBytes = count; - while (true) - { - int bytesRead = inf.Inflate(buffer, offset, remainingBytes); - offset += bytesRead; - remainingBytes -= bytesRead; - - if (remainingBytes == 0 || inf.IsFinished) - { - break; - } - - if (inf.IsNeedingInput) - { - Fill(); - } - else if (bytesRead == 0) - { - throw new ImageFormatException("Dont know what to do"); - } - } - return count - remainingBytes; - } - #endregion - - #region Instance Fields - /// - /// Decompressor for this stream - /// - protected Inflater inf; - - /// - /// Input buffer for this stream. - /// - protected InflaterInputBuffer inputBuffer; - - /// - /// Base stream the inflater reads from. - /// - private Stream baseInputStream; - - /// - /// The compressed size - /// - protected long csize; - - /// - /// Flag indicating wether this instance has been closed or not. - /// - bool isClosed; - - /// - /// Flag indicating wether this instance is designated the stream owner. - /// When closing if this flag is true the underlying stream is closed. - /// - bool isStreamOwner = true; - #endregion - } -} - diff --git a/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs b/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs deleted file mode 100644 index 353bebadd..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/OutputWindow.cs +++ /dev/null @@ -1,217 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - /// - /// Contains the output from the Inflation process. - /// We need to have a window so that we can refer backwards into the output stream - /// to repeat stuff.
- /// Author of the original java version : John Leuner - ///
- public class OutputWindow - { - #region Constants - const int WindowSize = 1 << 15; - const int WindowMask = WindowSize - 1; - #endregion - - #region Instance Fields - byte[] window = new byte[WindowSize]; //The window is 2^15 bytes - int windowEnd; - int windowFilled; - #endregion - - /// - /// Write a byte to this output window - /// - /// value to write - /// - /// if window is full - /// - public void Write(int value) - { - if (windowFilled++ == WindowSize) - { - throw new InvalidOperationException("Window full"); - } - window[windowEnd++] = (byte)value; - windowEnd &= WindowMask; - } - - - private void SlowRepeat(int repStart, int length, int distance) - { - while (length-- > 0) - { - window[windowEnd++] = window[repStart++]; - windowEnd &= WindowMask; - repStart &= WindowMask; - } - } - - /// - /// Append a byte pattern already in the window itself - /// - /// length of pattern to copy - /// distance from end of window pattern occurs - /// - /// If the repeated data overflows the window - /// - public void Repeat(int length, int distance) - { - if ((windowFilled += length) > WindowSize) - { - throw new InvalidOperationException("Window full"); - } - - int repStart = (windowEnd - distance) & WindowMask; - int border = WindowSize - length; - if ((repStart <= border) && (windowEnd < border)) - { - if (length <= distance) - { - System.Array.Copy(window, repStart, window, windowEnd, length); - windowEnd += length; - } - else - { - // We have to copy manually, since the repeat pattern overlaps. - while (length-- > 0) - { - window[windowEnd++] = window[repStart++]; - } - } - } - else - { - SlowRepeat(repStart, length, distance); - } - } - - /// - /// Copy from input manipulator to internal window - /// - /// source of data - /// length of data to copy - /// the number of bytes copied - public int CopyStored(StreamManipulator input, int length) - { - length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes); - int copied; - - int tailLen = WindowSize - windowEnd; - if (length > tailLen) - { - copied = input.CopyBytes(window, windowEnd, tailLen); - if (copied == tailLen) - { - copied += input.CopyBytes(window, 0, length - tailLen); - } - } - else - { - copied = input.CopyBytes(window, windowEnd, length); - } - - windowEnd = (windowEnd + copied) & WindowMask; - windowFilled += copied; - return copied; - } - - /// - /// Copy dictionary to window - /// - /// source dictionary - /// offset of start in source dictionary - /// length of dictionary - /// - /// If window isnt empty - /// - public void CopyDict(byte[] dictionary, int offset, int length) - { - if (dictionary == null) - { - throw new ArgumentNullException("dictionary"); - } - - if (windowFilled > 0) - { - throw new InvalidOperationException(); - } - - if (length > WindowSize) - { - offset += length - WindowSize; - length = WindowSize; - } - System.Array.Copy(dictionary, offset, window, 0, length); - windowEnd = length & WindowMask; - } - - /// - /// Get remaining unfilled space in window - /// - /// Number of bytes left in window - public int GetFreeSpace() - { - return WindowSize - windowFilled; - } - - /// - /// Get bytes available for output in window - /// - /// Number of bytes filled - public int GetAvailable() - { - return windowFilled; - } - - /// - /// Copy contents of window to output - /// - /// buffer to copy to - /// offset to start at - /// number of bytes to count - /// The number of bytes copied - /// - /// If a window underflow occurs - /// - public int CopyOutput(byte[] output, int offset, int len) - { - int copyEnd = windowEnd; - if (len > windowFilled) - { - len = windowFilled; - } - else - { - copyEnd = (windowEnd - windowFilled + len) & WindowMask; - } - - int copied = len; - int tailLen = len - copyEnd; - - if (tailLen > 0) - { - System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen); - offset += tailLen; - len = copyEnd; - } - System.Array.Copy(window, copyEnd - len, output, offset, len); - windowFilled -= copied; - if (windowFilled < 0) - { - throw new InvalidOperationException(); - } - return copied; - } - - /// - /// Reset by clearing window so GetAvailable returns 0 - /// - public void Reset() - { - windowFilled = windowEnd = 0; - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs deleted file mode 100644 index 001558f4a..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/PendingBuffer.cs +++ /dev/null @@ -1,217 +0,0 @@ -namespace ImageProcessor.Formats -{ - /// - /// This class is general purpose class for writing data to a buffer. - /// - /// It allows you to write bits as well as bytes - /// Based on DeflaterPending.java - /// - /// author of the original java version : Jochen Hoenicke - /// - public class PendingBuffer - { - #region Instance Fields - /// - /// Internal work buffer - /// - byte[] buffer_; - - int start; - int end; - - uint bits; - int bitCount; - #endregion - - #region Constructors - /// - /// construct instance using default buffer size of 4096 - /// - public PendingBuffer() : this(4096) - { - } - - /// - /// construct instance using specified buffer size - /// - /// - /// size to use for internal buffer - /// - public PendingBuffer(int bufferSize) - { - buffer_ = new byte[bufferSize]; - } - - #endregion - - /// - /// Clear internal state/buffers - /// - public void Reset() - { - start = end = bitCount = 0; - } - - /// - /// Write a byte to buffer - /// - /// - /// The value to write - /// - public void WriteByte(int value) - { - buffer_[end++] = unchecked((byte)value); - } - - /// - /// Write a short value to buffer LSB first - /// - /// - /// The value to write. - /// - public void WriteShort(int value) - { - buffer_[end++] = unchecked((byte)value); - buffer_[end++] = unchecked((byte)(value >> 8)); - } - - /// - /// write an integer LSB first - /// - /// The value to write. - public void WriteInt(int value) - { - buffer_[end++] = unchecked((byte)value); - buffer_[end++] = unchecked((byte)(value >> 8)); - buffer_[end++] = unchecked((byte)(value >> 16)); - buffer_[end++] = unchecked((byte)(value >> 24)); - } - - /// - /// Write a block of data to buffer - /// - /// data to write - /// offset of first byte to write - /// number of bytes to write - public void WriteBlock(byte[] block, int offset, int length) - { - System.Array.Copy(block, offset, buffer_, end, length); - end += length; - } - - /// - /// The number of bits written to the buffer - /// - public int BitCount - { - get - { - return bitCount; - } - } - - /// - /// Align internal buffer on a byte boundary - /// - public void AlignToByte() - { - if (bitCount > 0) - { - buffer_[end++] = unchecked((byte)bits); - if (bitCount > 8) - { - buffer_[end++] = unchecked((byte)(bits >> 8)); - } - } - bits = 0; - bitCount = 0; - } - - /// - /// Write bits to internal buffer - /// - /// source of bits - /// number of bits to write - public void WriteBits(int b, int count) - { - bits |= (uint)(b << bitCount); - bitCount += count; - if (bitCount >= 16) - { - buffer_[end++] = unchecked((byte)bits); - buffer_[end++] = unchecked((byte)(bits >> 8)); - bits >>= 16; - bitCount -= 16; - } - } - - /// - /// Write a short value to internal buffer most significant byte first - /// - /// value to write - public void WriteShortMSB(int s) - { - buffer_[end++] = unchecked((byte)(s >> 8)); - buffer_[end++] = unchecked((byte)s); - } - - /// - /// Indicates if buffer has been flushed - /// - public bool IsFlushed - { - get - { - return end == 0; - } - } - - /// - /// Flushes the pending buffer into the given output array. If the - /// output array is to small, only a partial flush is done. - /// - /// The output array. - /// The offset into output array. - /// The maximum number of bytes to store. - /// The number of bytes flushed. - public int Flush(byte[] output, int offset, int length) - { - if (bitCount >= 8) - { - buffer_[end++] = unchecked((byte)bits); - bits >>= 8; - bitCount -= 8; - } - - if (length > end - start) - { - length = end - start; - System.Array.Copy(buffer_, start, output, offset, length); - start = 0; - end = 0; - } - else - { - System.Array.Copy(buffer_, start, output, offset, length); - start += length; - } - return length; - } - - /// - /// Convert internal buffer to byte array. - /// Buffer is empty on completion - /// - /// - /// The internal buffer contents converted to a byte array. - /// - public byte[] ToByteArray() - { - byte[] result = new byte[end - start]; - System.Array.Copy(buffer_, start, result, 0, result.Length); - start = 0; - end = 0; - return result; - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/README.md b/src/ImageProcessor/Formats/Png/Zlib/README.md index eca351910..c297a91d5 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/README.md +++ b/src/ImageProcessor/Formats/Png/Zlib/README.md @@ -1,2 +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. +Adler32.cs and Crc32.cs have been copied from +https://github.com/ygrenier/SharpZipLib.Portable diff --git a/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs b/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs deleted file mode 100644 index 1f934558d..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/StreamManipulator.cs +++ /dev/null @@ -1,279 +0,0 @@ -namespace ImageProcessor.Formats -{ - using System; - - /// - /// This class allows us to retrieve a specified number of bits from - /// the input buffer, as well as copy big byte blocks. - /// - /// It uses an int buffer to store up to 31 bits for direct - /// manipulation. This guarantees that we can get at least 16 bits, - /// but we only need at most 15, so this is all safe. - /// - /// There are some optimizations in this class, for example, you must - /// never peek more than 8 bits more than needed, and you must first - /// peek bits before you may drop them. This is not a general purpose - /// class but optimized for the behaviour of the Inflater. - /// - /// authors of the original java version : John Leuner, Jochen Hoenicke - /// - public class StreamManipulator - { - #region Constructors - /// - /// Constructs a default StreamManipulator with all buffers empty - /// - public StreamManipulator() - { - } - #endregion - - /// - /// Get the next sequence of bits but don't increase input pointer. bitCount must be - /// less or equal 16 and if this call succeeds, you must drop - /// at least n - 8 bits in the next call. - /// - /// The number of bits to peek. - /// - /// the value of the bits, or -1 if not enough bits available. */ - /// - public int PeekBits(int bitCount) - { - if (bitsInBuffer_ < bitCount) - { - if (windowStart_ == windowEnd_) - { - return -1; // ok - } - buffer_ |= (uint)((window_[windowStart_++] & 0xff | - (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_); - bitsInBuffer_ += 16; - } - return (int)(buffer_ & ((1 << bitCount) - 1)); - } - - /// - /// Drops the next n bits from the input. You should have called PeekBits - /// with a bigger or equal n before, to make sure that enough bits are in - /// the bit buffer. - /// - /// The number of bits to drop. - public void DropBits(int bitCount) - { - buffer_ >>= bitCount; - bitsInBuffer_ -= bitCount; - } - - /// - /// Gets the next n bits and increases input pointer. This is equivalent - /// to followed by , except for correct error handling. - /// - /// The number of bits to retrieve. - /// - /// the value of the bits, or -1 if not enough bits available. - /// - public int GetBits(int bitCount) - { - int bits = PeekBits(bitCount); - if (bits >= 0) - { - DropBits(bitCount); - } - return bits; - } - - /// - /// Gets the number of bits available in the bit buffer. This must be - /// only called when a previous PeekBits() returned -1. - /// - /// - /// the number of bits available. - /// - public int AvailableBits - { - get - { - return bitsInBuffer_; - } - } - - /// - /// Gets the number of bytes available. - /// - /// - /// The number of bytes available. - /// - public int AvailableBytes - { - get - { - return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3); - } - } - - /// - /// Skips to the next byte boundary. - /// - public void SkipToByteBoundary() - { - buffer_ >>= (bitsInBuffer_ & 7); - bitsInBuffer_ &= ~7; - } - - /// - /// Returns true when SetInput can be called - /// - public bool IsNeedingInput - { - get - { - return windowStart_ == windowEnd_; - } - } - - /// - /// Copies bytes from input buffer to output buffer starting - /// at output[offset]. You have to make sure, that the buffer is - /// byte aligned. If not enough bytes are available, copies fewer - /// bytes. - /// - /// - /// The buffer to copy bytes to. - /// - /// - /// The offset in the buffer at which copying starts - /// - /// - /// The length to copy, 0 is allowed. - /// - /// - /// The number of bytes copied, 0 if no bytes were available. - /// - /// - /// Length is less than zero - /// - /// - /// Bit buffer isnt byte aligned - /// - public int CopyBytes(byte[] output, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException("length"); - } - - if ((bitsInBuffer_ & 7) != 0) - { - // bits_in_buffer may only be 0 or a multiple of 8 - throw new InvalidOperationException("Bit buffer is not byte aligned!"); - } - - int count = 0; - while ((bitsInBuffer_ > 0) && (length > 0)) - { - output[offset++] = (byte)buffer_; - buffer_ >>= 8; - bitsInBuffer_ -= 8; - length--; - count++; - } - - if (length == 0) - { - return count; - } - - int avail = windowEnd_ - windowStart_; - if (length > avail) - { - length = avail; - } - System.Array.Copy(window_, windowStart_, output, offset, length); - windowStart_ += length; - - if (((windowStart_ - windowEnd_) & 1) != 0) - { - // We always want an even number of bytes in input, see peekBits - buffer_ = (uint)(window_[windowStart_++] & 0xff); - bitsInBuffer_ = 8; - } - return count + length; - } - - /// - /// Resets state and empties internal buffers - /// - public void Reset() - { - buffer_ = 0; - windowStart_ = windowEnd_ = bitsInBuffer_ = 0; - } - - /// - /// Add more input for consumption. - /// Only call when IsNeedingInput returns true - /// - /// data to be input - /// offset of first byte of input - /// number of bytes of input to add. - public void SetInput(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (offset < 0) - { -#if NETCF_1_0 - throw new ArgumentOutOfRangeException("offset"); -#else - throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); -#endif - } - - if (count < 0) - { -#if NETCF_1_0 - throw new ArgumentOutOfRangeException("count"); -#else - throw new ArgumentOutOfRangeException("count", "Cannot be negative"); -#endif - } - - if (windowStart_ < windowEnd_) - { - throw new InvalidOperationException("Old input was not completely processed"); - } - - int end = offset + count; - - // We want to throw an ArrayIndexOutOfBoundsException early. - // Note the check also handles integer wrap around. - if ((offset > end) || (end > buffer.Length)) - { - throw new ArgumentOutOfRangeException("count"); - } - - if ((count & 1) != 0) - { - // We always want an even number of bytes in input, see PeekBits - buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_); - bitsInBuffer_ += 8; - } - - window_ = buffer; - windowStart_ = offset; - windowEnd_ = end; - } - - #region Instance Fields - private byte[] window_; - private int windowStart_; - private int windowEnd_; - - private uint buffer_; - private int bitsInBuffer_; - #endregion - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs deleted file mode 100644 index 3ac686aad..000000000 --- a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs +++ /dev/null @@ -1,263 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Formats -{ - using System.Text; - - /// - /// This class contains constants used for Zip format files - /// - public static class ZipConstants - { - /// - /// The version made by field for entries in the central header when created by this library - /// - /// - /// This is also the Zip version for the library when comparing against the version required to extract - /// for an entry. - public const int VersionMadeBy = 51; // was 45 before AES - - /// - /// The minimum version required to support strong encryption - /// - public const int VersionStrongEncryption = 50; - - /// - /// Version indicating AES encryption - /// - public const int VERSION_AES = 51; - - /// - /// The version required for Zip64 extensions (4.5 or higher) - /// - public const int VersionZip64 = 45; - - /// - /// Size of local entry header (excluding variable length fields at end) - /// - public const int LocalHeaderBaseSize = 30; - - /// - /// Size of Zip64 data descriptor - /// - public const int Zip64DataDescriptorSize = 24; - - /// - /// Size of data descriptor - /// - public const int DataDescriptorSize = 16; - - /// - /// Size of central header entry (excluding variable fields) - /// - public const int CentralHeaderBaseSize = 46; - - /// - /// Size of end of central record (excluding variable fields) - /// - public const int EndOfCentralRecordBaseSize = 22; - - /// - /// Size of 'classic' cryptographic header stored before any entry data - /// - public const int CryptoHeaderSize = 12; - - /// - /// Signature for local entry header - /// - public const int LocalHeaderSignature = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); - - /// - /// Signature for spanning entry - /// - public const int SpanningSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for temporary spanning entry - /// - public const int SpanningTempSignature = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); - - /// - /// Signature for data descriptor - /// - /// - /// This is only used where the length, Crc, or compressed size isnt known when the - /// entry is created and the output stream doesnt support seeking. - /// The local entry cannot be 'patched' with the correct values in this case - /// so the values are recorded after the data prefixed by this header, as well as in the central directory. - /// - public const int DataDescriptorSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for central header - /// - public const int CentralHeaderSignature = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); - - /// - /// Signature for Zip64 central file header - /// - public const int Zip64CentralFileHeaderSignature = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); - - /// - /// Signature for Zip64 central directory locator - /// - public const int Zip64CentralDirLocatorSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); - - /// - /// Signature for archive extra data signature (were headers are encrypted). - /// - public const int ArchiveExtraDataSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); - - /// - /// Central header digitial signature - /// - public const int CentralHeaderDigitalSignature = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); - - /// - /// End of central directory record signature - /// - public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); - - /// - /// PCL don't support CodePage so we used Encoding instead of - /// - public static Encoding DefaultEncoding { get; set; } = Encoding.UTF8; - - /// - /// Convert a portion of a byte array to a string. - /// - /// - /// Data to convert to string - /// - /// - /// Number of bytes to convert starting from index 0 - /// - /// - /// data[0]..data[count - 1] converted to a string - /// - public static string ConvertToString(byte[] data, int count) - { - if (data == null) - { - return string.Empty; - } - - return DefaultEncoding.GetString(data, 0, count); - } - - /// - /// Convert a byte array to string - /// - /// - /// Byte array to convert - /// - /// - /// dataconverted to a string - /// - public static string ConvertToString(byte[] data) - { - if (data == null) - { - return string.Empty; - } - - return ConvertToString(data, data.Length); - } - - /// - /// Convert a byte array to string - /// - /// The applicable general purpose bits flags - /// - /// Byte array to convert - /// - /// The number of bytes to convert. - /// - /// dataconverted to a string - /// - public static string ConvertToStringExt(int flags, byte[] data, int count) - { - if (data == null) - { - return string.Empty; - } - - if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) - { - return Encoding.UTF8.GetString(data, 0, count); - } - else - { - return ConvertToString(data, count); - } - } - - /// - /// Convert a byte array to string - /// - /// The applicable general purpose bits flags - /// Byte array to convert - /// - /// dataconverted to a string - /// - public static string ConvertToStringExt(int flags, byte[] data) - { - if (data == null) - { - return string.Empty; - } - - if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) - { - return Encoding.UTF8.GetString(data, 0, data.Length); - } - else - { - return ConvertToString(data, data.Length); - } - } - - /// - /// Convert a string to a byte array - /// - /// - /// String to convert to an array - /// - /// Converted array - public static byte[] ConvertToArray(string str) - { - if (str == null) - { - return new byte[0]; - } - - return DefaultEncoding.GetBytes(str); - } - - /// - /// Convert a string to a byte array - /// - /// The applicable general purpose bits flags - /// - /// String to convert to an array - /// - /// Converted array - public static byte[] ConvertToArray(int flags, string str) - { - if (str == null) - { - return new byte[0]; - } - - if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) - { - return Encoding.UTF8.GetBytes(str); - } - - return ConvertToArray(str); - } - } -} diff --git a/src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibInputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/ZlibInputStream.cs similarity index 98% rename from src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibInputStream.cs rename to src/ImageProcessor/Formats/Png/Zlib/ZlibInputStream.cs index 129042bda..bcbd5d3bd 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibInputStream.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/ZlibInputStream.cs @@ -5,7 +5,7 @@ namespace ImageProcessor.Formats using System.IO; using System.IO.Compression; - internal class ZlibInputStream : Stream + internal sealed class ZlibInputStream : Stream { /// /// A value indicating whether this instance of the given entity has been disposed. @@ -146,7 +146,7 @@ namespace ImageProcessor.Formats for (int i = 0; i < 4; i++) { // we dont really check/use this - this.crcread[i] = (byte)rawStream.ReadByte(); + this.crcread[i] = (byte)this.rawStream.ReadByte(); } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibOutputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/ZlibOutputStream.cs similarity index 95% rename from src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibOutputStream.cs rename to src/ImageProcessor/Formats/Png/Zlib/ZlibOutputStream.cs index 258c13f0e..9af156d06 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibOutputStream.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/ZlibOutputStream.cs @@ -1,11 +1,15 @@ - +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageProcessor.Formats { using System; using System.IO; using System.IO.Compression; - internal class ZlibOutputStream : Stream + internal sealed class ZlibOutputStream : Stream { /// /// The raw stream containing the uncompressed image data. @@ -48,8 +52,8 @@ namespace ImageProcessor.Formats // 4-bit information field depending on the compression method. // bits 0 to 3 CM Compression method // bits 4 to 7 CINFO Compression info - // 0 1 // + // 0 1 // +---+---+ // |CMF|FLG| // +---+---+ @@ -95,8 +99,6 @@ namespace ImageProcessor.Formats level = CompressionLevel.NoCompression; } - // I must create with leaveopen=true always and do the closing myself, because MS implementation - // of DeflateStream: I cant force a flush of the underlying stream without closing. this.deflateStream = new DeflateStream(this.rawStream, level, true); } @@ -181,7 +183,7 @@ namespace ImageProcessor.Formats } else { - // Second hack: empty input? + // Hack: empty input? this.rawStream.WriteByte(3); this.rawStream.WriteByte(0); } diff --git a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs index baf56ed3b..374f00afb 100644 --- a/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Formats/EncoderDecoderTests.cs @@ -17,10 +17,10 @@ Directory.CreateDirectory("TestOutput/Encode"); } - foreach (FileInfo file in new DirectoryInfo("TestOutput/Encode").GetFiles()) - { - file.Delete(); - } + //foreach (FileInfo file in new DirectoryInfo("TestOutput/Encode").GetFiles()) + //{ + // file.Delete(); + //} foreach (string file in Files) { diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Png/mountain.png.REMOVED.git-id b/tests/ImageProcessor.Tests/TestImages/Formats/Png/mountain.png.REMOVED.git-id deleted file mode 100644 index e10578b3c..000000000 --- a/tests/ImageProcessor.Tests/TestImages/Formats/Png/mountain.png.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -e51bffc5ceb300a70039c9c3ce4542eff47925ea \ No newline at end of file