diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 2483a3ad9d..b944b43a34 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal sealed class PngChunk { + public PngChunk(int length) + { + this.Length = length; + } + /// - /// Gets or sets the length. + /// Gets the length. /// An unsigned integer giving the number of bytes in the chunk's /// data field. The length counts only the data field, not itself, /// the chunk type code, or the CRC. Zero is a valid length /// - public int Length { get; set; } + public int Length { get; } /// /// Gets or sets the chunk type as string with 4 chars. diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ffa3875057..e5431cf34f 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -217,19 +217,18 @@ namespace SixLabors.ImageSharp.Formats.Png { using (var deframeStream = new ZlibInflateStream(this.currentStream)) { - PngChunk currentChunk; - while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk)) { try { - switch (currentChunk.Type) + switch (chunk.Type) { case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data.Array); + this.ReadHeaderChunk(chunk.Data.Array); this.ValidateHeader(); break; case PngChunkTypes.Physical: - this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); + this.ReadPhysicalChunk(metadata, chunk.Data.Array); break; case PngChunkTypes.Data: if (image == null) @@ -237,23 +236,23 @@ namespace SixLabors.ImageSharp.Formats.Png this.InitializeImage(metadata, out image); } - deframeStream.AllocateNewBytes(currentChunk.Length); + deframeStream.AllocateNewBytes(chunk.Length); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.currentStream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: - byte[] pal = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length); + byte[] pal = new byte[chunk.Length]; + Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); this.palette = pal; break; case PngChunkTypes.PaletteAlpha: - byte[] alpha = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length); + byte[] alpha = new byte[chunk.Length]; + Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha); break; case PngChunkTypes.Text: - this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); + this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; @@ -263,10 +262,10 @@ namespace SixLabors.ImageSharp.Formats.Png finally { // Data is rented in ReadChunkData() - if (currentChunk.Data != null) + if (chunk.Data != null) { - currentChunk.Data.Dispose(); - currentChunk.Data = null; + chunk.Data.Dispose(); + chunk.Data = null; } } } @@ -297,25 +296,24 @@ namespace SixLabors.ImageSharp.Formats.Png this.currentStream.Skip(8); try { - PngChunk currentChunk; - while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk)) { try { - switch (currentChunk.Type) + switch (chunk.Type) { case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data.Array); + this.ReadHeaderChunk(chunk.Data.Array); this.ValidateHeader(); break; case PngChunkTypes.Physical: - this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); + this.ReadPhysicalChunk(metadata, chunk.Data.Array); break; case PngChunkTypes.Data: - this.SkipChunkDataAndCrc(currentChunk); + this.SkipChunkDataAndCrc(chunk); break; case PngChunkTypes.Text: - this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); + this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; @@ -325,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Png finally { // Data is rented in ReadChunkData() - if (currentChunk.Data != null) + if (chunk.Data != null) { - ArrayPool.Shared.Return(currentChunk.Data.Array); + ArrayPool.Shared.Return(chunk.Data.Array); } } } @@ -1208,36 +1206,40 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The . /// - private PngChunk ReadChunk() + private bool TryReadChunk(out PngChunk chunk) { - var chunk = new PngChunk(); - this.ReadChunkLength(chunk); + int length = this.ReadChunkLength(); - if (chunk.Length == -1) + if (length == -1) { + chunk = default; + // IEND - return null; + return false; } + chunk = new PngChunk(length); + if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position) { // Not a valid chunk so we skip back all but one of the four bytes we have just read. // That lets us read one byte at a time until we reach a known chunk. this.currentStream.Position -= 3; - return chunk; + + return true; } this.ReadChunkType(chunk); if (chunk.Type == PngChunkTypes.Data) { - return chunk; + return true; } this.ReadChunkData(chunk); this.ReadChunkCrc(chunk); - return chunk; + return true; } /// @@ -1314,21 +1316,19 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Calculates the length of the given chunk. /// - /// The chunk. /// /// Thrown if the input stream is not valid. /// - private void ReadChunkLength(PngChunk chunk) + private int ReadChunkLength() { int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); if (numBytes < 4) { - chunk.Length = -1; - return; + return -1; } - chunk.Length = BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer); + return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer); } ///