Browse Source

Create fine-grained options for CRC handling.

pull/2589/head
James Jackson-South 3 years ago
parent
commit
c99c83ae61
  1. 14
      src/ImageSharp/Formats/Png/PngChunk.cs
  2. 30
      src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs
  3. 18
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 5
      src/ImageSharp/Formats/Png/PngDecoderOptions.cs
  5. 2
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

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

@ -41,9 +41,13 @@ internal readonly struct PngChunk
/// <summary> /// <summary>
/// Gets a value indicating whether the given chunk is critical to decoding /// Gets a value indicating whether the given chunk is critical to decoding
/// </summary> /// </summary>
public bool IsCritical => /// <param name="handling">The chunk CRC handling behavior.</param>
this.Type is PngChunkType.Header or public bool IsCritical(PngCrcChunkHandling handling)
PngChunkType.Palette or => handling switch
PngChunkType.Data or {
PngChunkType.FrameData; PngCrcChunkHandling.IgnoreNone => true,
PngCrcChunkHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData,
PngCrcChunkHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette,
_ => false,
};
} }

30
src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png;
/// <summary>
/// Specifies how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
/// </summary>
public enum PngCrcChunkHandling
{
/// <summary>
/// Do not ignore any CRC chunk errors.
/// </summary>
IgnoreNone,
/// <summary>
/// Ignore CRC errors in non critical chunks.
/// </summary>
IgnoreNonCritical,
/// <summary>
/// Ignore CRC errors in data chunks.
/// </summary>
IgnoreData,
/// <summary>
/// Ignore CRC errors in all chunks.
/// </summary>
IgnoreAll
}

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

@ -115,9 +115,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
private PngChunk? nextChunk; private PngChunk? nextChunk;
/// <summary> /// <summary>
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored. /// How to handle CRC errors.
/// </summary> /// </summary>
private bool ignoreCrcErrors; private readonly PngCrcChunkHandling pngCrcChunkHandling;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class. /// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
@ -130,7 +130,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.maxFrames = options.GeneralOptions.MaxFrames; this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = options.GeneralOptions.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
this.ignoreCrcErrors = options.IgnoreCrcCheck; this.pngCrcChunkHandling = options.PngCrcChunkHandling;
} }
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly) internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
@ -141,7 +141,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.skipMetadata = true; this.skipMetadata = true;
this.configuration = options.GeneralOptions.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
this.ignoreCrcErrors = options.IgnoreCrcCheck; this.pngCrcChunkHandling = options.PngCrcChunkHandling;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -1797,11 +1797,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
type: type, type: type,
data: this.ReadChunkData(length)); data: this.ReadChunkData(length));
if (!this.ignoreCrcErrors) this.ValidateChunk(chunk, buffer);
{
this.ValidateChunk(chunk, buffer);
}
// Restore the stream position for IDAT and fdAT chunks, because it will be decoded later and // Restore the stream position for IDAT and fdAT chunks, because it will be decoded later and
// was only read to verifying the CRC is correct. // was only read to verifying the CRC is correct.
if (type is PngChunkType.Data or PngChunkType.FrameData) if (type is PngChunkType.Data or PngChunkType.FrameData)
@ -1820,8 +1817,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
private void ValidateChunk(in PngChunk chunk, Span<byte> buffer) private void ValidateChunk(in PngChunk chunk, Span<byte> buffer)
{ {
uint inputCrc = this.ReadChunkCrc(buffer); uint inputCrc = this.ReadChunkCrc(buffer);
if (chunk.IsCritical(this.pngCrcChunkHandling))
if (chunk.IsCritical)
{ {
Span<byte> chunkType = stackalloc byte[4]; Span<byte> chunkType = stackalloc byte[4];
BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type);

5
src/ImageSharp/Formats/Png/PngDecoderOptions.cs

@ -12,8 +12,7 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions
public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions(); public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions();
/// <summary> /// <summary>
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored. /// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
/// Similar to PNG_CRC_QUIET_USE in libpng.
/// </summary> /// </summary>
public bool IgnoreCrcCheck { get; init; } public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical;
} }

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

@ -475,7 +475,7 @@ public partial class PngDecoderTests
public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors<TPixel>(TestImageProvider<TPixel> provider) public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { IgnoreCrcCheck = true }); using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData });
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder(false)); image.CompareToOriginal(provider, new MagickReferenceDecoder(false));

Loading…
Cancel
Save