Browse Source

refactor PngDecoder to SpecializedImageDecoder<PngDecoderOptions>

pull/2589/head
Sven Claesson 3 years ago
parent
commit
72b8013241
  1. 17
      src/ImageSharp/Formats/Png/PngDecoder.cs
  2. 32
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 19
      src/ImageSharp/Formats/Png/PngDecoderOptions.cs
  4. 11
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  5. 5
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs

17
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
/// <summary> /// <summary>
/// Decoder for generating an image out of a png encoded stream. /// Decoder for generating an image out of a png encoded stream.
/// </summary> /// </summary>
public sealed class PngDecoder : ImageDecoder public sealed class PngDecoder : SpecializedImageDecoder<PngDecoderOptions>
{ {
private PngDecoder() private PngDecoder()
{ {
@ -25,31 +25,31 @@ public sealed class PngDecoder : ImageDecoder
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
return new PngDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); return new PngDecoderCore(new PngDecoderOptions() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected override Image<TPixel> Decode<TPixel>(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
PngDecoderCore decoder = new(options); PngDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken); Image<TPixel> image = decoder.Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
ScaleToTargetSize(options, image); ScaleToTargetSize(options.GeneralOptions, image);
return image; return image;
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) protected override Image Decode(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
PngDecoderCore decoder = new(options, true); PngDecoderCore decoder = new(options, true);
ImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken); ImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
stream.Position = 0; stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata(); PngMetadata meta = info.Metadata.GetPngMetadata();
@ -99,4 +99,7 @@ public sealed class PngDecoder : ImageDecoder
return this.Decode<Rgba32>(options, stream, cancellationToken); return this.Decode<Rgba32>(options, stream, cancellationToken);
} }
} }
/// <inheritdoc/>
protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new PngDecoderOptions() { GeneralOptions = options };
} }

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

@ -113,27 +113,34 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
private PngChunk? nextChunk; private PngChunk? nextChunk;
/// <summary>
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored.
/// </summary>
private bool ignoreCrcErrors;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class. /// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PngDecoderCore(DecoderOptions options) public PngDecoderCore(PngDecoderOptions options)
{ {
this.Options = options; this.Options = options.GeneralOptions;
this.configuration = options.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.maxFrames = options.MaxFrames; this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = options.SkipMetadata; this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
this.ignoreCrcErrors = options.IgnoreCrcCheck;
} }
internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly) internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
{ {
this.Options = options; this.Options = options.GeneralOptions;
this.colorMetadataOnly = colorMetadataOnly; this.colorMetadataOnly = colorMetadataOnly;
this.maxFrames = options.MaxFrames; this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = true; this.skipMetadata = true;
this.configuration = options.Configuration; this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.configuration.MemoryAllocator;
this.ignoreCrcErrors = options.IgnoreCrcCheck;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -1789,8 +1796,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
type: type, type: type,
data: this.ReadChunkData(length)); data: this.ReadChunkData(length));
this.ValidateChunk(chunk, buffer); if (!this.ignoreCrcErrors)
{
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)

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

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png;
/// <summary>
/// Configuration options for decoding png images.
/// </summary>
public sealed class PngDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions();
/// <summary>
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored.
/// Similar to PNG_CRC_QUIET_USE in libpng.
/// </summary>
public bool IgnoreCrcCheck { get; init; }
}

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

@ -470,6 +470,17 @@ public partial class PngDecoderTests
Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message); Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message);
} }
[Theory]
[WithFile(TestImages.Png.Bad.WrongCrcDataChunk, PixelTypes.Rgba32)]
public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { IgnoreCrcCheck = true });
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder(false));
}
// https://github.com/SixLabors/ImageSharp/issues/1014 // https://github.com/SixLabors/ImageSharp/issues/1014
[Theory] [Theory]
[WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)] [WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)]

5
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs

@ -31,12 +31,17 @@ public class MagickReferenceDecoder : ImageDecoder
{ {
IgnoreFileSize = !this.validate IgnoreFileSize = !this.validate
}; };
PngReadDefines pngReadDefines = new()
{
IgnoreCrc = !this.validate
};
MagickReadSettings settings = new() MagickReadSettings settings = new()
{ {
FrameCount = (int)options.MaxFrames FrameCount = (int)options.MaxFrames
}; };
settings.SetDefines(bmpReadDefines); settings.SetDefines(bmpReadDefines);
settings.SetDefines(pngReadDefines);
using MagickImageCollection magickImageCollection = new(stream, settings); using MagickImageCollection magickImageCollection = new(stream, settings);
List<ImageFrame<TPixel>> framesList = new(); List<ImageFrame<TPixel>> framesList = new();

Loading…
Cancel
Save