Browse Source

Merge branch 'master' into LightnessProcessor

pull/1000/head
ip75 7 years ago
committed by GitHub
parent
commit
4ed6ee4222
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 58
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 76
      src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
  3. 3
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  4. 1
      tests/ImageSharp.Tests/TestImages.cs
  5. BIN
      tests/Images/Input/Png/zlib-ztxt-bad-header.png

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

@ -5,6 +5,7 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -175,11 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image); this.InitializeImage(metadata, out image);
} }
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata);
{
deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata);
}
break; break;
case PngChunkType.Palette: case PngChunkType.Palette:
@ -465,19 +462,25 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Reads the scanlines within the image. /// Reads the scanlines within the image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="chunk">The png chunk containing the compressed scanline data.</param>
/// <param name="image"> The pixel data.</param> /// <param name="image"> The pixel data.</param>
/// <param name="pngMetadata">The png metadata</param> /// <param name="pngMetadata">The png metadata</param>
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image, PngMetadata pngMetadata) private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
}
else
{ {
this.DecodePixelData(dataStream, image, pngMetadata); deframeStream.AllocateNewBytes(chunk.Length, true);
DeflateStream dataStream = deframeStream.CompressedStream;
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
}
else
{
this.DecodePixelData(dataStream, image, pngMetadata);
}
} }
} }
@ -924,7 +927,11 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
ReadOnlySpan<byte> compressedData = data.Slice(zeroIndex + 2); ReadOnlySpan<byte> compressedData = data.Slice(zeroIndex + 2);
metadata.TextData.Add(new PngTextData(name, this.UncompressTextData(compressedData, PngConstants.Encoding), string.Empty, string.Empty));
if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed))
{
metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty));
}
} }
/// <summary> /// <summary>
@ -987,7 +994,11 @@ namespace SixLabors.ImageSharp.Formats.Png
if (compressionFlag == 1) if (compressionFlag == 1)
{ {
ReadOnlySpan<byte> compressedData = data.Slice(dataStartIdx); ReadOnlySpan<byte> compressedData = data.Slice(dataStartIdx);
metadata.TextData.Add(new PngTextData(keyword, this.UncompressTextData(compressedData, PngConstants.TranslatedEncoding), language, translatedKeyword));
if (this.TryUncompressTextData(compressedData, PngConstants.TranslatedEncoding, out string uncompressed))
{
metadata.TextData.Add(new PngTextData(keyword, uncompressed, language, translatedKeyword));
}
} }
else else
{ {
@ -1001,13 +1012,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
/// <param name="compressedData">Compressed text data bytes.</param> /// <param name="compressedData">Compressed text data bytes.</param>
/// <param name="encoding">The string encoding to use.</param> /// <param name="encoding">The string encoding to use.</param>
/// <returns>A string.</returns> /// <param name="value">The uncompressed value.</param>
private string UncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding) /// <returns>The <see cref="bool"/>.</returns>
private bool TryUncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding, out string value)
{ {
using (var memoryStream = new MemoryStream(compressedData.ToArray())) using (var memoryStream = new MemoryStream(compressedData.ToArray()))
using (var inflateStream = new ZlibInflateStream(memoryStream, () => 0)) using (var inflateStream = new ZlibInflateStream(memoryStream))
{ {
inflateStream.AllocateNewBytes(compressedData.Length); if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
{
value = null;
return false;
}
var uncompressedBytes = new List<byte>(); var uncompressedBytes = new List<byte>();
// Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here. // Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
@ -1018,7 +1035,8 @@ namespace SixLabors.ImageSharp.Formats.Png
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length); bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
} }
return encoding.GetString(uncompressedBytes.ToArray()); value = encoding.GetString(uncompressedBytes.ToArray());
return true;
} }
} }

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

@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private static readonly byte[] ChecksumBuffer = new byte[4]; private static readonly byte[] ChecksumBuffer = new byte[4];
/// <summary> /// <summary>
/// The inner raw memory stream /// A default delegate to get more data from the inner stream.
/// </summary> /// </summary>
private readonly Stream innerStream; private static readonly Func<int> GetDataNoOp = () => 0;
/// <summary> /// <summary>
/// The compressed stream sitting over the top of the deframer /// The inner raw memory stream
/// </summary> /// </summary>
private DeflateStream compressedStream; private readonly Stream innerStream;
/// <summary> /// <summary>
/// A value indicating whether this instance of the given entity has been disposed. /// A value indicating whether this instance of the given entity has been disposed.
@ -55,8 +55,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <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>
/// <param name="getData">A delegate to get more data from the inner stream</param> public ZlibInflateStream(Stream innerStream)
: this(innerStream, GetDataNoOp)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ZlibInflateStream"/> class.
/// </summary>
/// <param name="innerStream">The inner raw stream.</param>
/// <param name="getData">A delegate to get more data from the inner stream.</param>
public ZlibInflateStream(Stream innerStream, Func<int> getData) public ZlibInflateStream(Stream innerStream, Func<int> getData)
{ {
this.innerStream = innerStream; this.innerStream = innerStream;
@ -76,31 +85,32 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override long Length => throw new NotSupportedException(); public override long Length => throw new NotSupportedException();
/// <inheritdoc/> /// <inheritdoc/>
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
/// <summary> /// <summary>
/// Gets the compressed stream over the deframed inner stream /// Gets the compressed stream over the deframed inner stream
/// </summary> /// </summary>
public DeflateStream CompressedStream => this.compressedStream; public DeflateStream CompressedStream { get; private set; }
/// <summary> /// <summary>
/// Adds new bytes from a frame found in the original stream /// Adds new bytes from a frame found in the original stream
/// </summary> /// </summary>
/// <param name="bytes">blabla</param> /// <param name="bytes">blabla</param>
public void AllocateNewBytes(int bytes) /// <param name="isCriticalChunk">Whether the chunk to be inflated is a critical chunk.</param>
/// <returns>The <see cref="bool"/>.</returns>
public bool AllocateNewBytes(int bytes, bool isCriticalChunk)
{ {
this.currentDataRemaining = bytes; this.currentDataRemaining = bytes;
if (this.compressedStream is null) if (this.CompressedStream is null)
{ {
this.InitializeInflateStream(); return this.InitializeInflateStream(isCriticalChunk);
} }
return true;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void Flush() public override void Flush() => throw new NotSupportedException();
{
throw new NotSupportedException();
}
/// <inheritdoc/> /// <inheritdoc/>
public override int ReadByte() public override int ReadByte()
@ -182,10 +192,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (disposing) if (disposing)
{ {
// dispose managed resources // dispose managed resources
if (this.compressedStream != null) if (this.CompressedStream != null)
{ {
this.compressedStream.Dispose(); this.CompressedStream.Dispose();
this.compressedStream = null; this.CompressedStream = null;
} }
} }
@ -197,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true; this.isDisposed = true;
} }
private void InitializeInflateStream() private bool InitializeInflateStream(bool isCriticalChunk)
{ {
// Read the zlib header : http://tools.ietf.org/html/rfc1950 // Read the zlib header : http://tools.ietf.org/html/rfc1950
// CMF(Compression Method and flags) // CMF(Compression Method and flags)
@ -215,7 +225,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.currentDataRemaining -= 2; this.currentDataRemaining -= 2;
if (cmf == -1 || flag == -1) if (cmf == -1 || flag == -1)
{ {
return; return false;
} }
if ((cmf & 0x0F) == 8) if ((cmf & 0x0F) == 8)
@ -225,14 +235,28 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (cinfo > 7) if (cinfo > 7)
{ {
// Values of CINFO above 7 are not allowed in RFC1950. if (isCriticalChunk)
// CINFO is not defined in this specification for CM not equal to 8. {
throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}"); // Values of CINFO above 7 are not allowed in RFC1950.
// CINFO is not defined in this specification for CM not equal to 8.
throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}");
}
else
{
return false;
}
} }
} }
else else
{ {
throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}"); if (isCriticalChunk)
{
throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
}
else
{
return false;
}
} }
// The preset dictionary. // The preset dictionary.
@ -246,7 +270,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
} }
// Initialize the deflate Stream. // Initialize the deflate Stream.
this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); this.CompressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
return true;
} }
} }
} }

3
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
TestImages.Png.GrayAlpha8Bit, TestImages.Png.GrayAlpha8Bit,
TestImages.Png.Gray1BitTrans, TestImages.Png.Gray1BitTrans,
TestImages.Png.Bad.ZlibOverflow, TestImages.Png.Bad.ZlibOverflow,
TestImages.Png.Bad.ZlibOverflow2 TestImages.Png.Bad.ZlibOverflow2,
TestImages.Png.Bad.ZlibZtxtBadHeader,
}; };
public static readonly string[] TestImages48Bpp = public static readonly string[] TestImages48Bpp =

1
tests/ImageSharp.Tests/TestImages.cs

@ -90,6 +90,7 @@ namespace SixLabors.ImageSharp.Tests
public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string CorruptedChunk = "Png/big-corrupted-chunk.png";
public const string ZlibOverflow = "Png/zlib-overflow.png"; public const string ZlibOverflow = "Png/zlib-overflow.png";
public const string ZlibOverflow2 = "Png/zlib-overflow2.png"; public const string ZlibOverflow2 = "Png/zlib-overflow2.png";
public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png";
} }
public static readonly string[] All = public static readonly string[] All =

BIN
tests/Images/Input/Png/zlib-ztxt-bad-header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Loading…
Cancel
Save