Skip to content

Commit 72b8013

Browse files
author
Sven Claesson
committed
refactor PngDecoder to SpecializedImageDecoder<PngDecoderOptions>
1 parent afe2133 commit 72b8013

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

src/ImageSharp/Formats/Png/PngDecoder.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
88
/// <summary>
99
/// Decoder for generating an image out of a png encoded stream.
1010
/// </summary>
11-
public sealed class PngDecoder : ImageDecoder
11+
public sealed class PngDecoder : SpecializedImageDecoder<PngDecoderOptions>
1212
{
1313
private PngDecoder()
1414
{
@@ -25,31 +25,31 @@ protected override ImageInfo Identify(DecoderOptions options, Stream stream, Can
2525
Guard.NotNull(options, nameof(options));
2626
Guard.NotNull(stream, nameof(stream));
2727

28-
return new PngDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
28+
return new PngDecoderCore(new PngDecoderOptions() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
2929
}
3030

3131
/// <inheritdoc/>
32-
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
32+
protected override Image<TPixel> Decode<TPixel>(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
3333
{
3434
Guard.NotNull(options, nameof(options));
3535
Guard.NotNull(stream, nameof(stream));
3636

3737
PngDecoderCore decoder = new(options);
38-
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
38+
Image<TPixel> image = decoder.Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
3939

40-
ScaleToTargetSize(options, image);
40+
ScaleToTargetSize(options.GeneralOptions, image);
4141

4242
return image;
4343
}
4444

4545
/// <inheritdoc/>
46-
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
46+
protected override Image Decode(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
4747
{
4848
Guard.NotNull(options, nameof(options));
4949
Guard.NotNull(stream, nameof(stream));
5050

5151
PngDecoderCore decoder = new(options, true);
52-
ImageInfo info = decoder.Identify(options.Configuration, stream, cancellationToken);
52+
ImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
5353
stream.Position = 0;
5454

5555
PngMetadata meta = info.Metadata.GetPngMetadata();
@@ -99,4 +99,7 @@ protected override Image Decode(DecoderOptions options, Stream stream, Cancellat
9999
return this.Decode<Rgba32>(options, stream, cancellationToken);
100100
}
101101
}
102+
103+
/// <inheritdoc/>
104+
protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new PngDecoderOptions() { GeneralOptions = options };
102105
}

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,34 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
113113
/// </summary>
114114
private PngChunk? nextChunk;
115115

116+
/// <summary>
117+
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored.
118+
/// </summary>
119+
private bool ignoreCrcErrors;
120+
116121
/// <summary>
117122
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
118123
/// </summary>
119124
/// <param name="options">The decoder options.</param>
120-
public PngDecoderCore(DecoderOptions options)
125+
public PngDecoderCore(PngDecoderOptions options)
121126
{
122-
this.Options = options;
123-
this.configuration = options.Configuration;
124-
this.maxFrames = options.MaxFrames;
125-
this.skipMetadata = options.SkipMetadata;
127+
this.Options = options.GeneralOptions;
128+
this.configuration = options.GeneralOptions.Configuration;
129+
this.maxFrames = options.GeneralOptions.MaxFrames;
130+
this.skipMetadata = options.GeneralOptions.SkipMetadata;
126131
this.memoryAllocator = this.configuration.MemoryAllocator;
132+
this.ignoreCrcErrors = options.IgnoreCrcCheck;
127133
}
128134

129-
internal PngDecoderCore(DecoderOptions options, bool colorMetadataOnly)
135+
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
130136
{
131-
this.Options = options;
137+
this.Options = options.GeneralOptions;
132138
this.colorMetadataOnly = colorMetadataOnly;
133-
this.maxFrames = options.MaxFrames;
139+
this.maxFrames = options.GeneralOptions.MaxFrames;
134140
this.skipMetadata = true;
135-
this.configuration = options.Configuration;
141+
this.configuration = options.GeneralOptions.Configuration;
136142
this.memoryAllocator = this.configuration.MemoryAllocator;
143+
this.ignoreCrcErrors = options.IgnoreCrcCheck;
137144
}
138145

139146
/// <inheritdoc/>
@@ -1789,8 +1796,11 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
17891796
type: type,
17901797
data: this.ReadChunkData(length));
17911798

1792-
this.ValidateChunk(chunk, buffer);
1793-
1799+
if (!this.ignoreCrcErrors)
1800+
{
1801+
this.ValidateChunk(chunk, buffer);
1802+
}
1803+
17941804
// Restore the stream position for IDAT and fdAT chunks, because it will be decoded later and
17951805
// was only read to verifying the CRC is correct.
17961806
if (type is PngChunkType.Data or PngChunkType.FrameData)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats.Png;
5+
6+
/// <summary>
7+
/// Configuration options for decoding png images.
8+
/// </summary>
9+
public sealed class PngDecoderOptions : ISpecializedDecoderOptions
10+
{
11+
/// <inheritdoc/>
12+
public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions();
13+
14+
/// <summary>
15+
/// If true, ADLER32 checksum in the IDAT chunk as well as the chunk CRCs will be ignored.
16+
/// Similar to PNG_CRC_QUIET_USE in libpng.
17+
/// </summary>
18+
public bool IgnoreCrcCheck { get; init; }
19+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,17 @@ public void Decode_InvalidDataChunkCrc_ThrowsException<TPixel>(TestImageProvider
470470
Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message);
471471
}
472472

473+
[Theory]
474+
[WithFile(TestImages.Png.Bad.WrongCrcDataChunk, PixelTypes.Rgba32)]
475+
public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors<TPixel>(TestImageProvider<TPixel> provider)
476+
where TPixel : unmanaged, IPixel<TPixel>
477+
{
478+
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { IgnoreCrcCheck = true });
479+
480+
image.DebugSave(provider);
481+
image.CompareToOriginal(provider, new MagickReferenceDecoder(false));
482+
}
483+
473484
// https://github.com/SixLabors/ImageSharp/issues/1014
474485
[Theory]
475486
[WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)]

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream s
3131
{
3232
IgnoreFileSize = !this.validate
3333
};
34+
PngReadDefines pngReadDefines = new()
35+
{
36+
IgnoreCrc = !this.validate
37+
};
3438

3539
MagickReadSettings settings = new()
3640
{
3741
FrameCount = (int)options.MaxFrames
3842
};
3943
settings.SetDefines(bmpReadDefines);
44+
settings.SetDefines(pngReadDefines);
4045

4146
using MagickImageCollection magickImageCollection = new(stream, settings);
4247
List<ImageFrame<TPixel>> framesList = new();

0 commit comments

Comments
 (0)