Browse Source

Merge pull request #708 from cwensley/curtis/png-decoder-mono

Fix issue on mono <= 5.14 when reading multiple png data chunks.
af/merge-core
Anton Firsov 8 years ago
committed by GitHub
parent
commit
bf6029b4e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 146
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 48
      src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs

146
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -187,6 +187,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
private bool hasTrans; private bool hasTrans;
/// <summary>
/// The next chunk of data to return
/// </summary>
private PngChunk? nextChunk;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class. /// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary> /// </summary>
@ -223,67 +228,67 @@ namespace SixLabors.ImageSharp.Formats.Png
Image<TPixel> image = null; Image<TPixel> image = null;
try try
{ {
using (var deframeStream = new ZlibInflateStream(this.currentStream)) while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{ {
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk)) try
{ {
try switch (chunk.Type)
{ {
switch (chunk.Type) case PngChunkType.Header:
{ this.ReadHeaderChunk(pngMetaData, chunk.Data.Array);
case PngChunkType.Header: this.ValidateHeader();
this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); break;
this.ValidateHeader(); case PngChunkType.Physical:
break; this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan());
case PngChunkType.Physical: break;
this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); case PngChunkType.Gamma:
break; this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan());
case PngChunkType.Gamma: break;
this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); case PngChunkType.Data:
break; if (image is null)
case PngChunkType.Data: {
if (image is null) this.InitializeImage(metaData, out image);
{ }
this.InitializeImage(metaData, out image);
}
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{
deframeStream.AllocateNewBytes(chunk.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); }
break;
case PngChunkType.Palette: break;
byte[] pal = new byte[chunk.Length]; case PngChunkType.Palette:
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); byte[] pal = new byte[chunk.Length];
this.palette = pal; Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
break; this.palette = pal;
case PngChunkType.PaletteAlpha: break;
byte[] alpha = new byte[chunk.Length]; case PngChunkType.PaletteAlpha:
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); byte[] alpha = new byte[chunk.Length];
this.paletteAlpha = alpha; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.AssignTransparentMarkers(alpha); this.paletteAlpha = alpha;
break; this.AssignTransparentMarkers(alpha);
case PngChunkType.Text: break;
this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length); case PngChunkType.Text:
break; this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length);
case PngChunkType.Exif: break;
if (!this.ignoreMetadata) case PngChunkType.Exif:
{ if (!this.ignoreMetadata)
byte[] exifData = new byte[chunk.Length]; {
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length); byte[] exifData = new byte[chunk.Length];
metaData.ExifProfile = new ExifProfile(exifData); Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
} metaData.ExifProfile = new ExifProfile(exifData);
}
break;
case PngChunkType.End: break;
this.isEndChunkReached = true; case PngChunkType.End:
break; this.isEndChunkReached = true;
} break;
}
finally
{
chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
} }
} }
finally
{
chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
}
} }
if (image is null) if (image is null)
@ -1366,6 +1371,32 @@ namespace SixLabors.ImageSharp.Formats.Png
metadata.Properties.Add(new ImageProperty(name, value)); metadata.Properties.Add(new ImageProperty(name, value));
} }
/// <summary>
/// Reads the next data chunk.
/// </summary>
/// <returns>Count of bytes in the next data chunk, or 0 if there are no more data chunks left.</returns>
private int ReadNextDataChunk()
{
if (this.nextChunk != null)
{
return 0;
}
this.currentStream.Read(this.crcBuffer, 0, 4);
if (this.TryReadChunk(out PngChunk chunk))
{
if (chunk.Type == PngChunkType.Data)
{
return chunk.Length;
}
this.nextChunk = chunk;
}
return 0;
}
/// <summary> /// <summary>
/// Reads a chunk from the stream. /// Reads a chunk from the stream.
/// </summary> /// </summary>
@ -1375,6 +1406,15 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </returns> /// </returns>
private bool TryReadChunk(out PngChunk chunk) private bool TryReadChunk(out PngChunk chunk)
{ {
if (this.nextChunk != null)
{
chunk = this.nextChunk.Value;
this.nextChunk = null;
return true;
}
int length = this.ReadChunkLength(); int length = this.ReadChunkLength();
if (length == -1) if (length == -1)

48
src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs

@ -43,22 +43,24 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// Whether the crc value has been read. /// The current data remaining to be read
/// </summary> /// </summary>
private bool crcRead; private int currentDataRemaining;
/// <summary> /// <summary>
/// The current data remaining to be read /// Delegate to get more data once we've exhausted the current data remaining
/// </summary> /// </summary>
private int currentDataRemaining; private Func<int> getData;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ZlibInflateStream"/> class. /// Initializes a new instance of the <see cref="ZlibInflateStream"/> class.
/// </summary> /// </summary>
/// <param name="innerStream">The inner raw stream</param> /// <param name="innerStream">The inner raw stream</param>
public ZlibInflateStream(Stream innerStream) /// <param name="getData">A delegate to get more data from the inner stream</param>
public ZlibInflateStream(Stream innerStream, Func<int> getData)
{ {
this.innerStream = innerStream; this.innerStream = innerStream;
this.getData = getData;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -112,12 +114,36 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{ {
if (this.currentDataRemaining == 0) if (this.currentDataRemaining == 0)
{ {
return 0; // last buffer was read in its entirety, let's make sure we don't actually have more
this.currentDataRemaining = this.getData();
if (this.currentDataRemaining == 0)
{
return 0;
}
} }
int bytesToRead = Math.Min(count, this.currentDataRemaining); int bytesToRead = Math.Min(count, this.currentDataRemaining);
this.currentDataRemaining -= bytesToRead; this.currentDataRemaining -= bytesToRead;
return this.innerStream.Read(buffer, offset, bytesToRead); int bytesRead = this.innerStream.Read(buffer, offset, bytesToRead);
// keep reading data until we've reached the end of the stream or filled the buffer
while (this.currentDataRemaining == 0 && bytesRead < count)
{
this.currentDataRemaining = this.getData();
if (this.currentDataRemaining == 0)
{
return bytesRead;
}
offset += bytesRead;
bytesToRead = Math.Min(count - bytesRead, this.currentDataRemaining);
this.currentDataRemaining -= bytesToRead;
bytesRead += this.innerStream.Read(buffer, offset, bytesToRead);
}
return bytesRead;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -153,14 +179,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{ {
this.compressedStream.Dispose(); this.compressedStream.Dispose();
this.compressedStream = null; this.compressedStream = null;
if (!this.crcRead)
{
// Consume the trailing 4 bytes
this.innerStream.Read(ChecksumBuffer, 0, 4);
this.currentDataRemaining -= 4;
this.crcRead = true;
}
} }
} }

Loading…
Cancel
Save