Browse Source

Png now decodes using MS Deflate stream

Former-commit-id: d753c8b0bdb066f56e4e48423e7a58dc3702c03d
Former-commit-id: 019e60e30b144148b895910791ddd807f6e637d3
Former-commit-id: 68825fd0d9534498cf2ff589854e01e518e9d246
af/merge-core
James Jackson-South 10 years ago
parent
commit
39864c689f
  1. 5
      src/ImageProcessor/Formats/Png/PngDecoderCore.cs
  2. 13
      src/ImageProcessor/Formats/Png/PngEncoder.cs
  3. 2
      src/ImageProcessor/Formats/Png/Zlib/Crc32.cs
  4. 4
      src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs
  5. 210
      src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibInputStream.cs
  6. 205
      src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibOutputStream.cs

5
src/ImageProcessor/Formats/Png/PngDecoderCore.cs

@ -138,7 +138,7 @@ namespace ImageProcessor.Formats
if (this.header.Width > ImageBase.MaxWidth || this.header.Height > ImageBase.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input png '{this.header.Width}x{this.header.Height}' is bigger thean the "
$"The input png '{this.header.Width}x{this.header.Height}' is bigger than the "
+ $"max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
}
@ -260,7 +260,8 @@ namespace ImageProcessor.Formats
byte[] currentScanline = new byte[scanlineLength];
int filter = 0, column = -1;
using (InflaterInputStream compressedStream = new InflaterInputStream(dataStream))
using (ZlibInputStream compressedStream = new ZlibInputStream(dataStream))
//using (InflaterInputStream compressedStream = new InflaterInputStream(dataStream))
{
int readByte;
while ((readByte = compressedStream.ReadByte()) >= 0)

13
src/ImageProcessor/Formats/Png/PngEncoder.cs

@ -38,6 +38,13 @@ namespace ImageProcessor.Formats
/// <inheritdoc/>
public string Extension => "png";
/// <summary>
/// The compression level 1-9.
/// TODO: Get other compression levels to work. Something is cutting of image content.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets a value indicating whether this encoder
/// will write the image uncompressed the stream.
@ -56,7 +63,7 @@ namespace ImageProcessor.Formats
/// </summary>
/// <value>
/// <c>True</c> if this instance is writing gamma
/// information to the stream.; otherwise, <c>false</c>.
/// information to the stream; otherwise, <c>false</c>.
/// </value>
public bool IsWritingGamma { get; set; }
@ -95,7 +102,7 @@ namespace ImageProcessor.Formats
0x0D, // Line ending CRLF
0x0A, // Line ending CRLF
0x1A, // EOF
0x0A // LF
0x0A // LF
},
0,
8);
@ -360,6 +367,8 @@ namespace ImageProcessor.Formats
{
memoryStream = new MemoryStream();
// TODO: Get this working!
//using (ZlibOutputStream outputStream = new ZlibOutputStream(memoryStream, this.CompressionLevel))
using (DeflaterOutputStream outputStream = new DeflaterOutputStream(memoryStream))
{
outputStream.Write(data, 0, data.Length);

2
src/ImageProcessor/Formats/Png/Zlib/Crc32.cs

@ -104,7 +104,7 @@ namespace ImageProcessor.Formats
private uint crc;
/// <summary>
/// Returns the CRC32 data checksum computed so far.
/// Gets or sets the CRC32 data checksum computed so far.
/// </summary>
public long Value
{

4
src/ImageProcessor/Formats/Png/Zlib/IChecksum.cs

@ -15,7 +15,7 @@ namespace ImageProcessor.Formats
public interface IChecksum
{
/// <summary>
/// Returns the data checksum computed so far.
/// Gets the data checksum computed so far.
/// </summary>
long Value
{
@ -31,7 +31,7 @@ namespace ImageProcessor.Formats
/// Adds one byte to the data checksum.
/// </summary>
/// <param name = "value">
/// the data value to add. The high byte of the int is ignored.
/// The data value to add. The high byte of the integer is ignored.
/// </param>
void Update(int value);

210
src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibInputStream.cs

@ -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;
}
}
}

205
src/ImageProcessor/Formats/Png/Zlib/NewFolder/ZlibOutputStream.cs

@ -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…
Cancel
Save