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> /// </summary>
internal sealed class PngChunk internal sealed class PngChunk
{ {
public PngChunk(int length)
{
this.Length = length;
}
/// <summary> /// <summary>
/// Gets or sets the length. /// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's /// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself, /// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length /// the chunk type code, or the CRC. Zero is a valid length
/// </summary> /// </summary>
public int Length { get; set; } public int Length { get; }
/// <summary> /// <summary>
/// Gets or sets the chunk type as string with 4 chars. /// 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)) using (var deframeStream = new ZlibInflateStream(this.currentStream))
{ {
PngChunk currentChunk; while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{ {
try try
{ {
switch (currentChunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkTypes.Data:
if (image == null) if (image == null)
@ -237,23 +236,23 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image); this.InitializeImage(metadata, out image);
} }
deframeStream.AllocateNewBytes(currentChunk.Length); deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4); this.currentStream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal; this.palette = pal;
break; break;
case PngChunkTypes.PaletteAlpha: case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length]; byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha); this.AssignTransparentMarkers(alpha);
break; break;
case PngChunkTypes.Text: case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkTypes.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
@ -263,10 +262,10 @@ namespace SixLabors.ImageSharp.Formats.Png
finally finally
{ {
// Data is rented in ReadChunkData() // Data is rented in ReadChunkData()
if (currentChunk.Data != null) if (chunk.Data != null)
{ {
currentChunk.Data.Dispose(); chunk.Data.Dispose();
currentChunk.Data = null; chunk.Data = null;
} }
} }
} }
@ -297,25 +296,24 @@ namespace SixLabors.ImageSharp.Formats.Png
this.currentStream.Skip(8); this.currentStream.Skip(8);
try try
{ {
PngChunk currentChunk; while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{ {
try try
{ {
switch (currentChunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk); this.SkipChunkDataAndCrc(chunk);
break; break;
case PngChunkTypes.Text: case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkTypes.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
@ -325,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Png
finally finally
{ {
// Data is rented in ReadChunkData() // 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> /// <returns>
/// The <see cref="PngChunk"/>. /// The <see cref="PngChunk"/>.
/// </returns> /// </returns>
private PngChunk ReadChunk() private bool TryReadChunk(out PngChunk chunk)
{ {
var chunk = new PngChunk(); int length = this.ReadChunkLength();
this.ReadChunkLength(chunk);
if (chunk.Length == -1) if (length == -1)
{ {
chunk = default;
// IEND // IEND
return null; return false;
} }
chunk = new PngChunk(length);
if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position) 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. // 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. // That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3; this.currentStream.Position -= 3;
return chunk;
return true;
} }
this.ReadChunkType(chunk); this.ReadChunkType(chunk);
if (chunk.Type == PngChunkTypes.Data) if (chunk.Type == PngChunkTypes.Data)
{ {
return chunk; return true;
} }
this.ReadChunkData(chunk); this.ReadChunkData(chunk);
this.ReadChunkCrc(chunk); this.ReadChunkCrc(chunk);
return chunk; return true;
} }
/// <summary> /// <summary>
@ -1314,21 +1316,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Calculates the length of the given chunk. /// Calculates the length of the given chunk.
/// </summary> /// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid. /// Thrown if the input stream is not valid.
/// </exception> /// </exception>
private void ReadChunkLength(PngChunk chunk) private int ReadChunkLength()
{ {
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes < 4) if (numBytes < 4)
{ {
chunk.Length = -1; return -1;
return;
} }
chunk.Length = BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer); return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
} }
/// <summary> /// <summary>

Loading…
Cancel
Save