Skip to content

Commit 98437a0

Browse files
Merge pull request #2786 from SixLabors/js/rework-2589
Replace PngCrcChunkHandling
2 parents 350e439 + d0fbaeb commit 98437a0

File tree

7 files changed

+62
-48
lines changed

7 files changed

+62
-48
lines changed

src/ImageSharp/Formats/DecoderOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,10 @@ public sealed class DecoderOptions
5555
/// </summary>
5656
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
5757

58+
/// <summary>
59+
/// Gets the segment error handling strategy to use during decoding.
60+
/// </summary>
61+
public SegmentIntegrityHandling SegmentIntegrityHandling { get; init; } = SegmentIntegrityHandling.IgnoreNonCritical;
62+
5863
internal void SetConfiguration(Configuration configuration) => this.configuration = configuration;
5964
}

src/ImageSharp/Formats/Png/PngChunk.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ public PngChunk(int length, PngChunkType type, IMemoryOwner<byte> data = null)
4141
/// <summary>
4242
/// Gets a value indicating whether the given chunk is critical to decoding
4343
/// </summary>
44-
/// <param name="handling">The chunk CRC handling behavior.</param>
45-
public bool IsCritical(PngCrcChunkHandling handling)
44+
/// <param name="handling">The segment handling behavior.</param>
45+
public bool IsCritical(SegmentIntegrityHandling handling)
4646
=> handling switch
4747
{
48-
PngCrcChunkHandling.IgnoreNone => true,
49-
PngCrcChunkHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData,
50-
PngCrcChunkHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette,
48+
SegmentIntegrityHandling.IgnoreNone => true,
49+
SegmentIntegrityHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData,
50+
SegmentIntegrityHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette,
5151
_ => false,
5252
};
5353
}

src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
119119
/// <summary>
120120
/// How to handle CRC errors.
121121
/// </summary>
122-
private readonly PngCrcChunkHandling pngCrcChunkHandling;
122+
private readonly SegmentIntegrityHandling segmentIntegrityHandling;
123123

124124
/// <summary>
125125
/// A reusable Crc32 hashing instance.
@@ -142,7 +142,7 @@ public PngDecoderCore(PngDecoderOptions options)
142142
this.maxFrames = options.GeneralOptions.MaxFrames;
143143
this.skipMetadata = options.GeneralOptions.SkipMetadata;
144144
this.memoryAllocator = this.configuration.MemoryAllocator;
145-
this.pngCrcChunkHandling = options.PngCrcChunkHandling;
145+
this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling;
146146
this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
147147
}
148148

@@ -154,7 +154,7 @@ internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
154154
this.skipMetadata = true;
155155
this.configuration = options.GeneralOptions.Configuration;
156156
this.memoryAllocator = this.configuration.MemoryAllocator;
157-
this.pngCrcChunkHandling = options.PngCrcChunkHandling;
157+
this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling;
158158
this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
159159
}
160160

@@ -833,7 +833,7 @@ private void DecodePixelData<TPixel>(
833833
break;
834834

835835
default:
836-
if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll)
836+
if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll)
837837
{
838838
goto EXIT;
839839
}
@@ -939,7 +939,7 @@ private void DecodeInterlacedPixelData<TPixel>(
939939
break;
940940

941941
default:
942-
if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll)
942+
if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll)
943943
{
944944
goto EXIT;
945945
}
@@ -1927,7 +1927,7 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
19271927
private void ValidateChunk(in PngChunk chunk, Span<byte> buffer)
19281928
{
19291929
uint inputCrc = this.ReadChunkCrc(buffer);
1930-
if (chunk.IsCritical(this.pngCrcChunkHandling))
1930+
if (chunk.IsCritical(this.segmentIntegrityHandling))
19311931
{
19321932
Span<byte> chunkType = stackalloc byte[4];
19331933
BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type);

src/ImageSharp/Formats/Png/PngDecoderOptions.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions
1111
/// <inheritdoc/>
1212
public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions();
1313

14-
/// <summary>
15-
/// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
16-
/// </summary>
17-
public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical;
18-
1914
/// <summary>
2015
/// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
2116
/// Defaults to 8MB
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Formats;
5+
6+
/// <summary>
7+
/// Specifies how to handle validation of errors in different segments of encoded image files.
8+
/// </summary>
9+
public enum SegmentIntegrityHandling
10+
{
11+
/// <summary>
12+
/// Do not ignore any errors.
13+
/// </summary>
14+
IgnoreNone,
15+
16+
/// <summary>
17+
/// Ignore errors in non-critical segments of the encoded image.
18+
/// </summary>
19+
IgnoreNonCritical,
20+
21+
/// <summary>
22+
/// Ignore errors in data segments (e.g., image data, metadata).
23+
/// </summary>
24+
IgnoreData,
25+
26+
/// <summary>
27+
/// Ignore errors in all segments.
28+
/// </summary>
29+
IgnoreAll
30+
}

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,20 @@ public void Identify(string imagePath, int expectedPixelSize)
381381
Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
382382
}
383383

384+
[Theory]
385+
[InlineData(TestImages.Png.Bad.WrongCrcDataChunk, 1)]
386+
[InlineData(TestImages.Png.Bad.Issue2589, 24)]
387+
public void Identify_IgnoreCrcErrors(string imagePath, int expectedPixelSize)
388+
{
389+
TestFile testFile = TestFile.Create(imagePath);
390+
using MemoryStream stream = new(testFile.Bytes, false);
391+
392+
ImageInfo imageInfo = Image.Identify(new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData }, stream);
393+
394+
Assert.NotNull(imageInfo);
395+
Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
396+
}
397+
384398
[Theory]
385399
[WithFile(TestImages.Png.Bad.MissingDataChunk, PixelTypes.Rgba32)]
386400
public void Decode_MissingDataChunk_ThrowsException<TPixel>(TestImageProvider<TPixel> provider)
@@ -479,7 +493,7 @@ public void Decode_InvalidDataChunkCrc_ThrowsException<TPixel>(TestImageProvider
479493
public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors<TPixel>(TestImageProvider<TPixel> provider, bool compare)
480494
where TPixel : unmanaged, IPixel<TPixel>
481495
{
482-
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData });
496+
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance, new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData });
483497

484498
image.DebugSave(provider);
485499
if (compare)
@@ -660,7 +674,7 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr)
660674
public void Binary_PrematureEof()
661675
{
662676
PngDecoder decoder = PngDecoder.Instance;
663-
PngDecoderOptions options = new() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData };
677+
PngDecoderOptions options = new() { GeneralOptions = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData } };
664678
using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446, decoder, options);
665679

666680
// TODO: Try to reduce this to 1.

0 commit comments

Comments
 (0)