Browse Source

!st pass cleanup of Deflater

pull/1054/head
James Jackson-South 7 years ago
parent
commit
884ff6db9f
  1. 573
      src/ImageSharp/Formats/Png/Zlib/Deflater.cs
  2. 17
      src/ImageSharp/Formats/Png/Zlib/DeflaterThrowHelper.cs
  3. 9
      src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

573
src/ImageSharp/Formats/Png/Zlib/Deflater.cs

@ -1,97 +1,92 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// <auto-generated/>
using System; using System;
using System.Collections.Generic; using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png.Zlib namespace SixLabors.ImageSharp.Formats.Png.Zlib
{ {
/// <summary> /// <summary>
/// This is the Deflater class. The deflater class compresses input /// This class compresses input with the deflate algorithm described in RFC 1951.
/// with the deflate algorithm described in RFC 1951. It has several /// It has several compression levels and three different strategies described below.
/// compression levels and three different strategies described below.
///
/// This class is <i>not</i> thread safe. This is inherent in the API, due
/// to the split of deflate and setInput.
///
/// author of the original java version : Jochen Hoenicke
/// </summary> /// </summary>
public sealed class Deflater : IDisposable public sealed class Deflater : IDisposable
{ {
#region Deflater Documentation
/*
* The Deflater can do the following state transitions:
*
* (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
* / | (2) (5) |
* / v (5) |
* (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
* \ | (3) | ,--------'
* | | | (3) /
* v v (5) v v
* (1) -> BUSY_STATE ----> FINISHING_STATE
* | (6)
* v
* FINISHED_STATE
* \_____________________________________/
* | (7)
* v
* CLOSED_STATE
*
* (1) If we should produce a header we start in INIT_STATE, otherwise
* we start in BUSY_STATE.
* (2) A dictionary may be set only when we are in INIT_STATE, then
* we change the state as indicated.
* (3) Whether a dictionary is set or not, on the first call of deflate
* we change to BUSY_STATE.
* (4) -- intentionally left blank -- :)
* (5) FINISHING_STATE is entered, when flush() is called to indicate that
* there is no more INPUT. There are also states indicating, that
* the header wasn't written yet.
* (6) FINISHED_STATE is entered, when everything has been flushed to the
* internal pending output buffer.
* (7) At any time (7)
*
*/
#endregion Deflater Documentation
#region Public Constants
/// <summary> /// <summary>
/// The best and slowest compression level. This tries to find very /// The best and slowest compression level. This tries to find very
/// long and distant string repetitions. /// long and distant string repetitions.
/// </summary> /// </summary>
public const int BEST_COMPRESSION = 9; public const int BestCompression = 9;
/// <summary> /// <summary>
/// The worst but fastest compression level. /// The worst but fastest compression level.
/// </summary> /// </summary>
public const int BEST_SPEED = 1; public const int BestSpeed = 1;
/// <summary> /// <summary>
/// The default compression level. /// The default compression level.
/// </summary> /// </summary>
public const int DEFAULT_COMPRESSION = -1; public const int DefaultCompression = -1;
/// <summary> /// <summary>
/// This level won't compress at all but output uncompressed blocks. /// This level won't compress at all but output uncompressed blocks.
/// </summary> /// </summary>
public const int NO_COMPRESSION = 0; public const int NoCompression = 0;
/// <summary> /// <summary>
/// The compression method. This is the only method supported so far. /// The compression method. This is the only method supported so far.
/// There is no need to use this constant at all. /// There is no need to use this constant at all.
/// </summary> /// </summary>
public const int DEFLATED = 8; public const int Deflated = 8;
#endregion Public Constants /// <summary>
/// Compression level.
/// </summary>
private int level;
/// <summary>
/// The current state.
/// </summary>
private int state;
private DeflaterPendingBuffer pending;
private DeflaterEngine engine;
private bool isDisposed;
#region Public Enum private const int IsSetDict = 0x01;
private const int IsFlushing = 0x04;
private const int IsFinishing = 0x08;
private const int BusyState = 0x10;
private const int FlushingState = 0x14;
private const int FinishingState = 0x1c;
private const int FinishedState = 0x1e;
private const int ClosedState = 0x7f;
/// <summary>
/// Initializes a new instance of the <see cref="Deflater"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator to use for buffer allocations.</param>
/// <param name="level">The compression level, a value between NoCompression and BestCompression.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">if level is out of range.</exception>
public Deflater(MemoryAllocator memoryAllocator, int level)
{
if (level == DefaultCompression)
{
level = 6;
}
else if (level < NoCompression || level > BestCompression)
{
throw new ArgumentOutOfRangeException(nameof(level));
}
this.pending = new DeflaterPendingBuffer(memoryAllocator);
this.engine = new DeflaterEngine(this.pending, true);
this.engine.Strategy = DeflateStrategy.Default;
this.SetLevel(level);
this.Reset();
}
/// <summary> /// <summary>
/// Compression Level as an enum for safer use /// Compression Level as an enum for safer use
@ -102,202 +97,72 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// The best and slowest compression level. This tries to find very /// The best and slowest compression level. This tries to find very
/// long and distant string repetitions. /// long and distant string repetitions.
/// </summary> /// </summary>
BEST_COMPRESSION = Deflater.BEST_COMPRESSION, BestCompression = Deflater.BestCompression,
/// <summary> /// <summary>
/// The worst but fastest compression level. /// The worst but fastest compression level.
/// </summary> /// </summary>
BEST_SPEED = Deflater.BEST_SPEED, BestSpeed = Deflater.BestSpeed,
/// <summary> /// <summary>
/// The default compression level. /// The default compression level.
/// </summary> /// </summary>
DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION, DefaultCompression = Deflater.DefaultCompression,
/// <summary> /// <summary>
/// This level won't compress at all but output uncompressed blocks. /// This level won't compress at all but output uncompressed blocks.
/// </summary> /// </summary>
NO_COMPRESSION = Deflater.NO_COMPRESSION, NoCompression = Deflater.NoCompression,
/// <summary> /// <summary>
/// The compression method. This is the only method supported so far. /// The compression method. This is the only method supported so far.
/// There is no need to use this constant at all. /// There is no need to use this constant at all.
/// </summary> /// </summary>
DEFLATED = Deflater.DEFLATED Deflated = Deflater.Deflated
} }
#endregion Public Enum
#region Local Constants
private const int IS_SETDICT = 0x01;
private const int IS_FLUSHING = 0x04;
private const int IS_FINISHING = 0x08;
private const int INIT_STATE = 0x00;
private const int SETDICT_STATE = 0x01;
// private static int INIT_FINISHING_STATE = 0x08;
// private static int SETDICT_FINISHING_STATE = 0x09;
private const int BUSY_STATE = 0x10;
private const int FLUSHING_STATE = 0x14;
private const int FINISHING_STATE = 0x1c;
private const int FINISHED_STATE = 0x1e;
private const int CLOSED_STATE = 0x7f;
#endregion Local Constants
#region Constructors
/// <summary> /// <summary>
/// Creates a new deflater with given compression level. /// Gets a value indicating whetherthe stream was finished and no more output bytes
/// are available.
/// </summary> /// </summary>
/// <param name="memoryAllocator">The memory allocator to use for buffer allocations.</param> public bool IsFinished => (this.state == FinishedState) && this.pending.IsFlushed;
/// <param name="level">
/// the compression level, a value between NO_COMPRESSION
/// and BEST_COMPRESSION.
/// </param>
/// <param name="noZlibHeaderOrFooter">
/// 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.
/// </param>
/// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
public Deflater(MemoryAllocator memoryAllocator, int level, bool noZlibHeaderOrFooter)
{
if (level == DEFAULT_COMPRESSION)
{
level = 6;
}
else if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
{
throw new ArgumentOutOfRangeException(nameof(level));
}
pending = new DeflaterPendingBuffer(memoryAllocator);
engine = new DeflaterEngine(pending, noZlibHeaderOrFooter);
this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
SetStrategy(DeflateStrategy.Default);
SetLevel(level);
Reset();
}
#endregion Constructors /// <summary>
/// Gets a value indicating whether the input buffer is empty.
/// You should then call setInput().
/// NOTE: This method can also return true when the stream
/// was finished.
/// </summary>
public bool IsNeedingInput => this.engine.NeedsInput();
/// <summary> /// <summary>
/// Resets the deflater. The deflater acts afterwards as if it was /// Resets the deflater. The deflater acts afterwards as if it was
/// just created with the same compression level and strategy as it /// just created with the same compression level and strategy as it
/// had before. /// had before.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void Reset() public void Reset()
{ {
state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE); this.state = BusyState;
totalOut = 0; this.pending.Reset();
pending.Reset(); this.engine.Reset();
engine.Reset();
}
/// <summary>
/// Gets the current adler checksum of the data that was processed so far.
/// </summary>
public int Adler
{
get
{
return engine.Adler;
}
}
/// <summary>
/// Gets the number of input bytes processed so far.
/// </summary>
public long TotalIn
{
get
{
return engine.TotalIn;
}
}
/// <summary>
/// Gets the number of output bytes so far.
/// </summary>
public long TotalOut
{
get
{
return totalOut;
}
} }
/// <summary> /// <summary>
/// Flushes the current input block. Further calls to deflate() will /// Flushes the current input block. Further calls to Deflate() will
/// produce enough output to inflate everything in the current input /// 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 /// block. It is used by DeflaterOutputStream to implement Flush().
/// private. It is used by DeflaterOutputStream to implement
/// flush().
/// </summary> /// </summary>
public void Flush() [MethodImpl(InliningOptions.ShortMethod)]
{ public void Flush() => this.state |= IsFlushing;
state |= IS_FLUSHING;
}
/// <summary> /// <summary>
/// Finishes the deflater with the current input block. It is an error /// Finishes the deflater with the current input block. It is an error
/// to give more input after this method was called. This method must /// to give more input after this method was called. This method must
/// be called to force all bytes to be flushed. /// be called to force all bytes to be flushed.
/// </summary> /// </summary>
public void Finish() [MethodImpl(InliningOptions.ShortMethod)]
{ public void Finish() => this.state |= IsFlushing | IsFinishing;
state |= (IS_FLUSHING | IS_FINISHING);
}
/// <summary>
/// Returns true if the stream was finished and no more output bytes
/// are available.
/// </summary>
public bool IsFinished
{
get
{
return (state == FINISHED_STATE) && pending.IsFlushed;
}
}
/// <summary>
/// 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.
/// </summary>
public bool IsNeedingInput
{
get
{
return engine.NeedsInput();
}
}
/// <summary>
/// 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 <code>setInput(input, 0, input.length)</code>.
/// </summary>
/// <param name="input">
/// the buffer containing the input data.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if the buffer was finished() or ended().
/// </exception>
public void SetInput(byte[] input)
{
SetInput(input, 0, input.Length);
}
/// <summary> /// <summary>
/// Sets the data which should be compressed next. This should be /// Sets the data which should be compressed next. This should be
@ -305,25 +170,21 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// The given byte array should not be changed, before needsInput() returns /// The given byte array should not be changed, before needsInput() returns
/// true again. /// true again.
/// </summary> /// </summary>
/// <param name="input"> /// <param name="input">The buffer containing the input data.</param>
/// the buffer containing the input data. /// <param name="offset">The start of the data.</param>
/// </param> /// <param name="count">The number of data bytes of input.</param>
/// <param name="offset"> /// <exception cref="InvalidOperationException">
/// the start of the data. /// if the buffer was finished or if previous input is still pending.
/// </param>
/// <param name="count">
/// the number of data bytes of input.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if the buffer was Finish()ed or if previous input is still pending.
/// </exception> /// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public void SetInput(byte[] input, int offset, int count) public void SetInput(byte[] input, int offset, int count)
{ {
if ((state & IS_FINISHING) != 0) if ((this.state & IsFinishing) != 0)
{ {
throw new InvalidOperationException("Finish() already called"); DeflaterThrowHelper.ThrowAlreadyFinished();
} }
engine.SetInput(input, offset, count);
this.engine.SetInput(input, offset, count);
} }
/// <summary> /// <summary>
@ -337,11 +198,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </param> /// </param>
public void SetLevel(int level) public void SetLevel(int level)
{ {
if (level == DEFAULT_COMPRESSION) if (level == DefaultCompression)
{ {
level = 6; level = 6;
} }
else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) else if (level < NoCompression || level > BestCompression)
{ {
throw new ArgumentOutOfRangeException(nameof(level)); throw new ArgumentOutOfRangeException(nameof(level));
} }
@ -349,273 +210,127 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (this.level != level) if (this.level != level)
{ {
this.level = level; this.level = level;
engine.SetLevel(level); this.engine.SetLevel(level);
} }
} }
/// <summary>
/// Get current compression level
/// </summary>
/// <returns>Returns the current compression level</returns>
public int GetLevel()
{
return level;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="strategy">
/// The new compression strategy.
/// </param>
public void SetStrategy(DeflateStrategy strategy)
{
engine.Strategy = strategy;
}
/// <summary>
/// Deflates the current input block with to the given array.
/// </summary>
/// <param name="output">
/// The buffer where compressed data is stored
/// </param>
/// <returns>
/// The number of compressed bytes added to the output, or 0 if either
/// IsNeedingInput() or IsFinished returns true or length is zero.
/// </returns>
public int Deflate(byte[] output)
{
return Deflate(output, 0, output.Length);
}
/// <summary> /// <summary>
/// Deflates the current input block to the given array. /// Deflates the current input block to the given array.
/// </summary> /// </summary>
/// <param name="output"> /// <param name="output">Buffer to store the compressed data.</param>
/// Buffer to store the compressed data. /// <param name="offset">Offset into the output array.</param>
/// </param> /// <param name="length">The maximum number of bytes that may be stored.</param>
/// <param name="offset">
/// Offset into the output array.
/// </param>
/// <param name="length">
/// The maximum number of bytes that may be stored.
/// </param>
/// <returns> /// <returns>
/// The number of compressed bytes added to the output, or 0 if either /// The number of compressed bytes added to the output, or 0 if either
/// needsInput() or finished() returns true or length is zero. /// <see cref="IsNeedingInput"/> or <see cref="IsFinished"/> returns true or length is zero.
/// </returns> /// </returns>
/// <exception cref="System.InvalidOperationException">
/// If Finish() was previously called.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// If offset or length don't match the array length.
/// </exception>
public int Deflate(byte[] output, int offset, int length) public int Deflate(byte[] output, int offset, int length)
{ {
int origLength = length; int origLength = length;
if (state == CLOSED_STATE) if (this.state == ClosedState)
{ {
throw new InvalidOperationException("Deflater closed"); DeflaterThrowHelper.ThrowAlreadyClosed();
} }
if (state < BUSY_STATE) if (this.state < BusyState)
{ {
// output header // Output header
int header = (DEFLATED + int header = (Deflated + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; int levelFlags = (this.level - 1) >> 1;
int level_flags = (level - 1) >> 1; if (levelFlags < 0 || levelFlags > 3)
if (level_flags < 0 || level_flags > 3)
{ {
level_flags = 3; levelFlags = 3;
} }
header |= level_flags << 6;
if ((state & IS_SETDICT) != 0) header |= levelFlags << 6;
if ((this.state & IsSetDict) != 0)
{ {
// Dictionary was set // Dictionary was set
header |= DeflaterConstants.PRESET_DICT; header |= DeflaterConstants.PRESET_DICT;
} }
header += 31 - (header % 31); header += 31 - (header % 31);
pending.WriteShortMSB(header); this.pending.WriteShortMSB(header);
if ((state & IS_SETDICT) != 0) if ((this.state & IsSetDict) != 0)
{ {
int chksum = engine.Adler; int chksum = this.engine.Adler;
engine.ResetAdler(); this.engine.ResetAdler();
pending.WriteShortMSB(chksum >> 16); this.pending.WriteShortMSB(chksum >> 16);
pending.WriteShortMSB(chksum & 0xffff); this.pending.WriteShortMSB(chksum & 0xffff);
} }
state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); this.state = BusyState | (this.state & (IsFlushing | IsFinishing));
} }
for (; ; ) while (true)
{ {
int count = pending.Flush(output, offset, length); int count = this.pending.Flush(output, offset, length);
offset += count; offset += count;
totalOut += count;
length -= count; length -= count;
if (length == 0 || state == FINISHED_STATE) if (length == 0 || this.state == FinishedState)
{ {
break; break;
} }
if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) if (!this.engine.Deflate((this.state & IsFlushing) != 0, (this.state & IsFinishing) != 0))
{ {
switch (state) switch (this.state)
{ {
case BUSY_STATE: case BusyState:
// We need more input now // We need more input now
return origLength - length; return origLength - length;
case FLUSHING_STATE: case FlushingState:
if (level != NO_COMPRESSION) if (this.level != NoCompression)
{ {
/* We have to supply some lookahead. 8 bit lookahead // We have to supply some lookahead. 8 bit lookahead
* is needed by the zlib inflater, and we must fill // is needed by the zlib inflater, and we must fill
* the next byte, so that all bits are flushed. // the next byte, so that all bits are flushed.
*/ int neededbits = 8 + ((-this.pending.BitCount) & 7);
int neededbits = 8 + ((-pending.BitCount) & 7);
while (neededbits > 0) while (neededbits > 0)
{ {
/* write a static tree block consisting solely of // Write a static tree block consisting solely of an EOF:
* an EOF: this.pending.WriteBits(2, 10);
*/
pending.WriteBits(2, 10);
neededbits -= 10; neededbits -= 10;
} }
} }
state = BUSY_STATE;
break;
case FINISHING_STATE: this.state = BusyState;
pending.AlignToByte(); break;
// Compressed data is complete. Write footer information if required. case FinishingState:
if (!noZlibHeaderOrFooter) this.pending.AlignToByte();
{ this.state = FinishedState;
int adler = engine.Adler;
pending.WriteShortMSB(adler >> 16);
pending.WriteShortMSB(adler & 0xffff);
}
state = FINISHED_STATE;
break; break;
} }
} }
} }
return origLength - length;
}
/// <summary> return origLength - length;
/// Sets the dictionary which should be used in the deflate process.
/// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.
/// </summary>
/// <param name="dictionary">
/// the dictionary.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if SetInput () or Deflate () were already called or another dictionary was already set.
/// </exception>
public void SetDictionary(byte[] dictionary)
{
SetDictionary(dictionary, 0, dictionary.Length);
} }
/// <summary> /// <inheritdoc/>
/// Sets the dictionary which should be used in the deflate process. public void Dispose()
/// 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.
/// </summary>
/// <param name="dictionary">
/// The dictionary data
/// </param>
/// <param name="index">
/// The index where dictionary information commences.
/// </param>
/// <param name="count">
/// The number of bytes in the dictionary.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// If SetInput () or Deflate() were already called or another dictionary was already set.
/// </exception>
public void SetDictionary(byte[] dictionary, int index, int count)
{ {
if (state != INIT_STATE) this.Dispose(true);
{ GC.SuppressFinalize(this);
throw new InvalidOperationException();
}
state = SETDICT_STATE;
engine.SetDictionary(dictionary, index, count);
} }
#region Instance Fields private void Dispose(bool disposing)
/// <summary>
/// Compression level.
/// </summary>
private int level;
/// <summary>
/// If true no Zlib/RFC1950 headers or footers are generated
/// </summary>
private bool noZlibHeaderOrFooter;
/// <summary>
/// The current state.
/// </summary>
private int state;
/// <summary>
/// The total bytes of output written.
/// </summary>
private long totalOut;
/// <summary>
/// The pending output.
/// </summary>
private DeflaterPendingBuffer pending;
/// <summary>
/// The deflater engine.
/// </summary>
private DeflaterEngine engine;
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
void Dispose(bool disposing)
{ {
if (!disposedValue) if (!this.isDisposed)
{ {
if (disposing) if (disposing)
{ {
this.pending.Dispose(); this.pending.Dispose();
// TODO: dispose managed state (managed objects).
} }
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
this.pending = null; this.pending = null;
disposedValue = true; this.isDisposed = true;
} }
} }
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#endregion Instance Fields
} }
} }

17
src/ImageSharp/Formats/Png/Zlib/DeflaterThrowHelper.cs

@ -0,0 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
internal static class DeflaterThrowHelper
{
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowAlreadyFinished() => throw new InvalidOperationException("Finish() already called.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed.");
}
}

9
src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

@ -3,7 +3,6 @@
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png.Zlib namespace SixLabors.ImageSharp.Formats.Png.Zlib
@ -65,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// +---+---+ // +---+---+
// |CMF|FLG| // |CMF|FLG|
// +---+---+ // +---+---+
int cmf = 0x78; const int Cmf = 0x78;
int flg = 218; int flg = 218;
// http://stackoverflow.com/a/2331025/277304 // http://stackoverflow.com/a/2331025/277304
@ -83,14 +82,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
} }
// Just in case // Just in case
flg -= ((cmf * 256) + flg) % 31; flg -= ((Cmf * 256) + flg) % 31;
if (flg < 0) if (flg < 0)
{ {
flg += 31; flg += 31;
} }
this.rawStream.WriteByte((byte)cmf); this.rawStream.WriteByte(Cmf);
this.rawStream.WriteByte((byte)flg); this.rawStream.WriteByte((byte)flg);
// Initialize the deflate Stream. // Initialize the deflate Stream.
@ -104,7 +103,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// { // {
// level = CompressionLevel.NoCompression; // level = CompressionLevel.NoCompression;
// } // }
this.deflater = new Deflater(memoryAllocator, compressionLevel, true); this.deflater = new Deflater(memoryAllocator, compressionLevel);
this.deflateStream = new DeflaterOutputStream(this.rawStream, this.deflater) { IsStreamOwner = false }; this.deflateStream = new DeflaterOutputStream(this.rawStream, this.deflater) { IsStreamOwner = false };
// this.deflateStream = new DeflateStream(this.rawStream, level, true); // this.deflateStream = new DeflateStream(this.rawStream, level, true);

Loading…
Cancel
Save