Browse Source

Use TryRead pattern for PngChunks

First step toward converting this to an immutable readonly struct
af/merge-core
Jason Nelson 8 years ago
parent
commit
804a12e2b1
  1. 9
      src/ImageSharp/Formats/Png/PngChunk.cs
  2. 72
      src/ImageSharp/Formats/Png/PngDecoderCore.cs

9
src/ImageSharp/Formats/Png/PngChunk.cs

@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngChunk
{
public PngChunk(int length)
{
this.Length = length;
}
/// <summary>
/// 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
/// </summary>
public int Length { get; set; }
public int Length { get; }
/// <summary>
/// Gets or sets the chunk type as string with 4 chars.

72
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<byte>.Shared.Return(currentChunk.Data.Array);
ArrayPool<byte>.Shared.Return(chunk.Data.Array);
}
}
}
@ -1208,36 +1206,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// The <see cref="PngChunk"/>.
/// </returns>
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;
}
/// <summary>
@ -1314,21 +1316,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Calculates the length of the given chunk.
/// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
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);
}
/// <summary>

Loading…
Cancel
Save