mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: d753c8b0bdb066f56e4e48423e7a58dc3702c03d Former-commit-id: 019e60e30b144148b895910791ddd807f6e637d3 Former-commit-id: 68825fd0d9534498cf2ff589854e01e518e9d246af/merge-core
6 changed files with 432 additions and 7 deletions
@ -0,0 +1,210 @@ |
|||||
|
|
||||
|
namespace ImageProcessor.Formats |
||||
|
{ |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.IO.Compression; |
||||
|
|
||||
|
internal class ZlibInputStream : Stream |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A value indicating whether this instance of the given entity has been disposed.
|
||||
|
/// </summary>
|
||||
|
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
||||
|
/// <remarks>
|
||||
|
/// If the entity is disposed, it must not be disposed a second
|
||||
|
/// time. The isDisposed field is set the first time the entity
|
||||
|
/// is disposed. If the isDisposed field is true, then the Dispose()
|
||||
|
/// method will not dispose again. This help not to prolong the entity's
|
||||
|
/// life in the Garbage Collector.
|
||||
|
/// </remarks>
|
||||
|
private bool isDisposed; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The raw stream containing the uncompressed image data.
|
||||
|
/// </summary>
|
||||
|
private readonly Stream rawStream; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The preset dictionary.
|
||||
|
/// Merely informational, not used.
|
||||
|
/// </summary>
|
||||
|
private bool fdict; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The DICT dictionary identifier identifying the used dictionary.
|
||||
|
/// Merely informational, not used.
|
||||
|
/// </summary>
|
||||
|
private byte[] dictId; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CINFO is the base-2 logarithm of the LZ77 window size, minus eight.
|
||||
|
/// Merely informational, not used.
|
||||
|
/// </summary>
|
||||
|
private int cinfo; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The read crc data.
|
||||
|
/// </summary>
|
||||
|
private byte[] crcread; |
||||
|
|
||||
|
// The stream responsible for decompressing the input stream.
|
||||
|
private DeflateStream deflateStream; |
||||
|
|
||||
|
public ZlibInputStream(Stream stream) |
||||
|
{ |
||||
|
this.rawStream = stream; |
||||
|
|
||||
|
// Read the zlib header : http://tools.ietf.org/html/rfc1950
|
||||
|
// CMF(Compression Method and flags)
|
||||
|
// This byte is divided into a 4 - bit compression method and a
|
||||
|
// 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
|
||||
|
// +---+---+
|
||||
|
// |CMF|FLG|
|
||||
|
// +---+---+
|
||||
|
int cmf = this.rawStream.ReadByte(); |
||||
|
int flag = this.rawStream.ReadByte(); |
||||
|
if (cmf == -1 || flag == -1) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if ((cmf & 0x0f) != 8) |
||||
|
{ |
||||
|
throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); |
||||
|
} |
||||
|
|
||||
|
this.cinfo = ((cmf & (0xf0)) >> 8); |
||||
|
this.fdict = (flag & 32) != 0; |
||||
|
|
||||
|
if (this.fdict) |
||||
|
{ |
||||
|
this.dictId = new byte[4]; |
||||
|
|
||||
|
for (int i = 0; i < 4; i++) |
||||
|
{ |
||||
|
// We consume but don't use this.
|
||||
|
this.dictId[i] = (byte)this.rawStream.ReadByte(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Initialize the deflate Stream.
|
||||
|
this.deflateStream = new DeflateStream(this.rawStream, CompressionMode.Decompress, true); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanRead => true; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanSeek => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanWrite => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Length |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Position |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void Flush() |
||||
|
{ |
||||
|
this.deflateStream?.Flush(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int Read(byte[] buffer, int offset, int count) |
||||
|
{ |
||||
|
// We dont't check CRC on reading
|
||||
|
int read = this.deflateStream.Read(buffer, offset, count); |
||||
|
if (read < 1 && this.crcread == null) |
||||
|
{ |
||||
|
// The deflater has ended. We try to read the next 4 bytes from raw stream (crc)
|
||||
|
this.crcread = new byte[4]; |
||||
|
for (int i = 0; i < 4; i++) |
||||
|
{ |
||||
|
// we dont really check/use this
|
||||
|
this.crcread[i] = (byte)rawStream.ReadByte(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return read; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Seek(long offset, SeekOrigin origin) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void SetLength(long value) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void Write(byte[] buffer, int offset, int count) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (this.isDisposed) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (disposing) |
||||
|
{ |
||||
|
// dispose managed resources
|
||||
|
if (this.deflateStream != null) |
||||
|
{ |
||||
|
this.deflateStream.Dispose(); |
||||
|
this.deflateStream = null; |
||||
|
|
||||
|
if (this.crcread == null) |
||||
|
{ |
||||
|
// Consume the trailing 4 bytes
|
||||
|
this.crcread = new byte[4]; |
||||
|
for (int i = 0; i < 4; i++) |
||||
|
{ |
||||
|
this.crcread[i] = (byte)this.rawStream.ReadByte(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
base.Dispose(disposing); |
||||
|
|
||||
|
// Call the appropriate methods to clean up
|
||||
|
// unmanaged resources here.
|
||||
|
// Note disposing is done.
|
||||
|
this.isDisposed = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,205 @@ |
|||||
|
|
||||
|
namespace ImageProcessor.Formats |
||||
|
{ |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.IO.Compression; |
||||
|
|
||||
|
internal class ZlibOutputStream : Stream |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The raw stream containing the uncompressed image data.
|
||||
|
/// </summary>
|
||||
|
private readonly Stream rawStream; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the checksum for the data stream.
|
||||
|
/// </summary>
|
||||
|
private readonly Adler32 adler32 = new Adler32(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A value indicating whether this instance of the given entity has been disposed.
|
||||
|
/// </summary>
|
||||
|
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
||||
|
/// <remarks>
|
||||
|
/// If the entity is disposed, it must not be disposed a second
|
||||
|
/// time. The isDisposed field is set the first time the entity
|
||||
|
/// is disposed. If the isDisposed field is true, then the Dispose()
|
||||
|
/// method will not dispose again. This help not to prolong the entity's
|
||||
|
/// life in the Garbage Collector.
|
||||
|
/// </remarks>
|
||||
|
private bool isDisposed; |
||||
|
|
||||
|
// The stream responsible for decompressing the input stream.
|
||||
|
private DeflateStream deflateStream; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of <see cref="ZlibOutputStream"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="stream">The stream to compress.</param>
|
||||
|
/// <param name="compressionLevel">The compression level.</param>
|
||||
|
public ZlibOutputStream(Stream stream, int compressionLevel) |
||||
|
{ |
||||
|
this.rawStream = stream; |
||||
|
|
||||
|
// Write the zlib header : http://tools.ietf.org/html/rfc1950
|
||||
|
// CMF(Compression Method and flags)
|
||||
|
// This byte is divided into a 4 - bit compression method and a
|
||||
|
// 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
|
||||
|
//
|
||||
|
// +---+---+
|
||||
|
// |CMF|FLG|
|
||||
|
// +---+---+
|
||||
|
int cmf = 0x78; |
||||
|
int flg = 218; |
||||
|
|
||||
|
// http://stackoverflow.com/a/2331025/277304
|
||||
|
if (compressionLevel >= 5 && compressionLevel <= 6) |
||||
|
{ |
||||
|
flg = 156; |
||||
|
} |
||||
|
else if (compressionLevel >= 3 && compressionLevel <= 4) |
||||
|
{ |
||||
|
flg = 94; |
||||
|
} |
||||
|
|
||||
|
else if (compressionLevel <= 2) |
||||
|
{ |
||||
|
flg = 1; |
||||
|
} |
||||
|
|
||||
|
// Just in case
|
||||
|
flg -= (cmf * 256 + flg) % 31; |
||||
|
|
||||
|
if (flg < 0) |
||||
|
{ |
||||
|
flg += 31; |
||||
|
} |
||||
|
|
||||
|
this.rawStream.WriteByte((byte)cmf); |
||||
|
this.rawStream.WriteByte((byte)flg); |
||||
|
|
||||
|
// Initialize the deflate Stream.
|
||||
|
CompressionLevel level = CompressionLevel.Optimal; |
||||
|
|
||||
|
if (compressionLevel >= 1 && compressionLevel <= 5) |
||||
|
{ |
||||
|
level = CompressionLevel.Fastest; |
||||
|
} |
||||
|
|
||||
|
else if (compressionLevel == 0) |
||||
|
{ |
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanRead => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanSeek => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool CanWrite => true; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Length |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Position |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void Flush() |
||||
|
{ |
||||
|
this.deflateStream?.Flush(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int Read(byte[] buffer, int offset, int count) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override long Seek(long offset, SeekOrigin origin) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void SetLength(long value) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void Write(byte[] buffer, int offset, int count) |
||||
|
{ |
||||
|
this.deflateStream.Write(buffer, offset, count); |
||||
|
this.adler32.Update(buffer, offset, count); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (this.isDisposed) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (disposing) |
||||
|
{ |
||||
|
// dispose managed resources
|
||||
|
if (this.deflateStream != null) |
||||
|
{ |
||||
|
this.deflateStream.Dispose(); |
||||
|
this.deflateStream = null; |
||||
|
} |
||||
|
else { |
||||
|
|
||||
|
// Second hack: empty input?
|
||||
|
this.rawStream.WriteByte(3); |
||||
|
this.rawStream.WriteByte(0); |
||||
|
} |
||||
|
|
||||
|
// Add the crc
|
||||
|
uint crc = (uint)this.adler32.Value; |
||||
|
this.rawStream.WriteByte((byte)((crc >> 24) & 0xFF)); |
||||
|
this.rawStream.WriteByte((byte)((crc >> 16) & 0xFF)); |
||||
|
this.rawStream.WriteByte((byte)((crc >> 8) & 0xFF)); |
||||
|
this.rawStream.WriteByte((byte)((crc) & 0xFF)); |
||||
|
} |
||||
|
|
||||
|
base.Dispose(disposing); |
||||
|
|
||||
|
// Call the appropriate methods to clean up
|
||||
|
// unmanaged resources here.
|
||||
|
// Note disposing is done.
|
||||
|
this.isDisposed = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue