diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index d5b5bfd0e..aece86700 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -141,7 +141,12 @@ namespace ImageSharp.Formats
///
/// The index of the current scanline being processed
///
- private int currentRow = 0;
+ private int currentRow = Adam7FirstRow[0];
+
+ ///
+ /// The current pass for an interlaced PNG
+ ///
+ private int pass = 0;
///
/// The current number of bytes read in the current scanline
@@ -487,82 +492,75 @@ namespace ImageSharp.Formats
private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels)
where TPixel : struct, IPixel
{
- byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
- byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
-
- try
+ while (this.pass < 7)
{
- for (int pass = 0; pass < 7; pass++)
+ int numColumns = this.ComputeColumnsAdam7(this.pass);
+
+ if (numColumns == 0)
{
- // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
- Array.Clear(scanline, 0, this.bytesPerScanline);
- Array.Clear(previousScanline, 0, this.bytesPerScanline);
+ // This pass contains no data; skip to next pass
+ continue;
+ }
- int y = Adam7FirstRow[pass];
- int numColumns = this.ComputeColumnsAdam7(pass);
+ int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1;
- if (numColumns == 0)
+ while (this.currentRow < this.header.Height)
+ {
+ int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
+ this.currentRowBytesRead += bytesRead;
+ if (this.currentRowBytesRead < bytesPerInterlaceScanline)
{
- // This pass contains no data; skip to next pass
- continue;
+ return;
}
- int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1;
-
- while (y < this.header.Height)
- {
- compressedStream.Read(scanline, 0, bytesPerInterlaceScanline);
+ this.currentRowBytesRead = 0;
- FilterType filterType = (FilterType)scanline[0];
+ FilterType filterType = (FilterType)this.scanline[0];
- switch (filterType)
- {
- case FilterType.None:
+ switch (filterType)
+ {
+ case FilterType.None:
- NoneFilter.Decode(scanline);
+ NoneFilter.Decode(this.scanline);
- break;
+ break;
- case FilterType.Sub:
+ case FilterType.Sub:
- SubFilter.Decode(scanline, bytesPerInterlaceScanline, this.bytesPerPixel);
+ SubFilter.Decode(this.scanline, bytesPerInterlaceScanline, this.bytesPerPixel);
- break;
+ break;
- case FilterType.Up:
+ case FilterType.Up:
- UpFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline);
+ UpFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline);
- break;
+ break;
- case FilterType.Average:
+ case FilterType.Average:
- AverageFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
+ AverageFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
- break;
+ break;
- case FilterType.Paeth:
+ case FilterType.Paeth:
- PaethFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
+ PaethFilter.Decode(this.scanline, this.previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
- break;
+ break;
- default:
- throw new ImageFormatException("Unknown filter type.");
- }
+ default:
+ throw new ImageFormatException("Unknown filter type.");
+ }
- this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]);
+ this.ProcessInterlacedDefilteredScanline(this.scanline, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
- Swap(ref scanline, ref previousScanline);
+ Swap(ref this.scanline, ref this.previousScanline);
- y += Adam7RowIncrement[pass];
- }
+ this.currentRow += Adam7RowIncrement[this.pass];
}
- }
- finally
- {
- ArrayPool.Shared.Return(previousScanline);
- ArrayPool.Shared.Return(scanline);
+
+ this.pass++;
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs
index e5101532c..69681d030 100644
--- a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs
@@ -52,8 +52,7 @@ namespace ImageSharp.Formats
/// checked separately. (Any sequence of zeroes has a Fletcher
/// checksum of zero.)"
///
- ///
- ///
+ ///
internal sealed class Adler32 : IChecksum
{
///
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeframeStream.cs b/src/ImageSharp/Formats/Png/Zlib/DeframeStream.cs
index 9b0a61b67..2e92fd52d 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeframeStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeframeStream.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+ using System.IO.Compression;
using System.Text;
///
@@ -18,7 +19,25 @@
///
/// The compressed stream sitting over the top of the deframer
///
- private ZlibInflateStream compressedStream;
+ private DeflateStream compressedStream;
+
+ ///
+ /// A value indicating whether this instance of the given entity has been disposed.
+ ///
+ /// if this instance has been disposed; otherwise, .
+ ///
+ /// 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.
+ ///
+ private bool isDisposed;
+
+ ///
+ /// The read crc data.
+ ///
+ private byte[] crcread;
///
/// The current data remaining to be read
@@ -52,7 +71,7 @@
///
/// Gets the compressed stream over the deframed inner stream
///
- public ZlibInflateStream CompressedStream => this.compressedStream;
+ public DeflateStream CompressedStream => this.compressedStream;
///
/// Adds new bytes from a frame found in the original stream
@@ -63,7 +82,7 @@
this.currentDataRemaining = bytes;
if (this.compressedStream == null)
{
- this.compressedStream = new ZlibInflateStream(this);
+ this.InitializeInflateStream();
}
}
@@ -114,8 +133,89 @@
///
protected override void Dispose(bool disposing)
{
- this.compressedStream.Dispose();
+ if (this.isDisposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ // dispose managed resources
+ if (this.compressedStream != null)
+ {
+ this.compressedStream.Dispose();
+ this.compressedStream = 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.innerStream.ReadByte();
+ }
+ }
+ }
+ }
+
base.Dispose(disposing);
+
+ // Call the appropriate methods to clean up
+ // unmanaged resources here.
+ // Note disposing is done.
+ this.isDisposed = true;
+ }
+
+ private void InitializeInflateStream()
+ {
+ // The DICT dictionary identifier identifying the used dictionary.
+
+ // The preset dictionary.
+ bool fdict;
+
+ // 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.innerStream.ReadByte();
+ int flag = this.innerStream.ReadByte();
+ this.currentDataRemaining -= 2;
+ if (cmf == -1 || flag == -1)
+ {
+ return;
+ }
+
+ if ((cmf & 0x0f) != 8)
+ {
+ throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}");
+ }
+
+ // CINFO is the base-2 logarithm of the LZ77 window size, minus eight.
+ // int cinfo = ((cmf & (0xf0)) >> 8);
+ fdict = (flag & 32) != 0;
+
+ if (fdict)
+ {
+ // The DICT dictionary identifier identifying the used dictionary.
+ byte[] dictId = new byte[4];
+
+ for (int i = 0; i < 4; i++)
+ {
+ // We consume but don't use this.
+ dictId[i] = (byte)this.innerStream.ReadByte();
+ this.currentDataRemaining--;
+ }
+ }
+
+ // Initialize the deflate Stream.
+ this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
index 2deb7dcf0..c1f04fa98 100644
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
@@ -40,7 +40,7 @@ namespace ImageSharp.Formats
///
/// The stream responsible for compressing the input stream.
///
- private DeflateStream deflateStream;
+ private System.IO.Compression.DeflateStream deflateStream;
///
/// Initializes a new instance of the class.
@@ -102,7 +102,7 @@ namespace ImageSharp.Formats
level = CompressionLevel.NoCompression;
}
- this.deflateStream = new DeflateStream(this.rawStream, level, true);
+ this.deflateStream = new System.IO.Compression.DeflateStream(this.rawStream, level, true);
}
///
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
deleted file mode 100644
index 977a4a167..000000000
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
+++ /dev/null
@@ -1,214 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Formats
-{
- using System;
- using System.IO;
- using System.IO.Compression;
-
- ///
- /// Provides methods and properties for decompressing streams by using the Zlib Deflate algorithm.
- ///
- internal sealed class ZlibInflateStream : Stream
- {
- ///
- /// The raw stream containing the uncompressed image data.
- ///
- private readonly Stream rawStream;
-
- ///
- /// A value indicating whether this instance of the given entity has been disposed.
- ///
- /// if this instance has been disposed; otherwise, .
- ///
- /// 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.
- ///
- private bool isDisposed;
-
- ///
- /// The read crc data.
- ///
- private byte[] crcread;
-
- ///
- /// The stream responsible for decompressing the input stream.
- ///
- private DeflateStream deflateStream;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The stream.
- ///
- /// Thrown if the compression method is incorrect.
- ///
- public ZlibInflateStream(Stream stream)
- {
- // The DICT dictionary identifier identifying the used dictionary.
-
- // The preset dictionary.
- bool fdict;
- 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}");
- }
-
- // CINFO is the base-2 logarithm of the LZ77 window size, minus eight.
- // int cinfo = ((cmf & (0xf0)) >> 8);
- fdict = (flag & 32) != 0;
-
- if (fdict)
- {
- // The DICT dictionary identifier identifying the used dictionary.
- byte[] dictId = new byte[4];
-
- for (int i = 0; i < 4; i++)
- {
- // We consume but don't use this.
- dictId[i] = (byte)this.rawStream.ReadByte();
- }
- }
-
- // Initialize the deflate Stream.
- this.deflateStream = new DeflateStream(this.rawStream, CompressionMode.Decompress, true);
- }
-
- ///
- public override bool CanRead => true;
-
- ///
- public override bool CanSeek => false;
-
- ///
- public override bool CanWrite => false;
-
- ///
- public override long Length
- {
- get
- {
- throw new NotSupportedException();
- }
- }
-
- ///
- public override long Position
- {
- get
- {
- throw new NotSupportedException();
- }
-
- set
- {
- throw new NotSupportedException();
- }
- }
-
- ///
- public override void Flush()
- {
- this.deflateStream?.Flush();
- }
-
- ///
- 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)this.rawStream.ReadByte();
- }
- }
-
- return read;
- }
-
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
-
- ///
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- ///
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
-
- ///
- 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;
- }
- }
-}