diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs
index 6243a071d5..3b16159b7e 100644
--- a/src/ImageSharp/Formats/DecoderOptions.cs
+++ b/src/ImageSharp/Formats/DecoderOptions.cs
@@ -55,5 +55,10 @@ public sealed class DecoderOptions
///
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
+ ///
+ /// Gets the segment error handling strategy to use during decoding.
+ ///
+ public SegmentIntegrityHandling SegmentIntegrityHandling { get; init; } = SegmentIntegrityHandling.IgnoreNonCritical;
+
internal void SetConfiguration(Configuration configuration) => this.configuration = configuration;
}
diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs
index 6ec0df9ad6..3883986d06 100644
--- a/src/ImageSharp/Formats/Png/PngChunk.cs
+++ b/src/ImageSharp/Formats/Png/PngChunk.cs
@@ -41,13 +41,13 @@ public PngChunk(int length, PngChunkType type, IMemoryOwner data = null)
///
/// Gets a value indicating whether the given chunk is critical to decoding
///
- /// The chunk CRC handling behavior.
- public bool IsCritical(PngCrcChunkHandling handling)
+ /// The segment handling behavior.
+ public bool IsCritical(SegmentIntegrityHandling handling)
=> handling switch
{
- 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,
+ SegmentIntegrityHandling.IgnoreNone => true,
+ SegmentIntegrityHandling.IgnoreNonCritical => this.Type is PngChunkType.Header or PngChunkType.Palette or PngChunkType.Data or PngChunkType.FrameData,
+ SegmentIntegrityHandling.IgnoreData => this.Type is PngChunkType.Header or PngChunkType.Palette,
_ => false,
};
}
diff --git a/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs b/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs
deleted file mode 100644
index 264d737fdc..0000000000
--- a/src/ImageSharp/Formats/Png/PngCrcChunkHandling.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-namespace SixLabors.ImageSharp.Formats.Png;
-
-///
-/// Specifies how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
-///
-public enum PngCrcChunkHandling
-{
- ///
- /// Do not ignore any CRC chunk errors.
- ///
- IgnoreNone,
-
- ///
- /// Ignore CRC errors in non critical chunks.
- ///
- IgnoreNonCritical,
-
- ///
- /// Ignore CRC errors in data chunks.
- ///
- IgnoreData,
-
- ///
- /// Ignore CRC errors in all chunks.
- ///
- IgnoreAll
-}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 01f038141a..484241d52f 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -119,7 +119,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
///
/// How to handle CRC errors.
///
- private readonly PngCrcChunkHandling pngCrcChunkHandling;
+ private readonly SegmentIntegrityHandling segmentIntegrityHandling;
///
/// A reusable Crc32 hashing instance.
@@ -142,7 +142,7 @@ public PngDecoderCore(PngDecoderOptions options)
this.maxFrames = options.GeneralOptions.MaxFrames;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator;
- this.pngCrcChunkHandling = options.PngCrcChunkHandling;
+ this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling;
this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
}
@@ -154,7 +154,7 @@ internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
this.skipMetadata = true;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
- this.pngCrcChunkHandling = options.PngCrcChunkHandling;
+ this.segmentIntegrityHandling = options.GeneralOptions.SegmentIntegrityHandling;
this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
}
@@ -833,7 +833,7 @@ private void DecodePixelData(
break;
default:
- if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll)
+ if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll)
{
goto EXIT;
}
@@ -939,7 +939,7 @@ private void DecodeInterlacedPixelData(
break;
default:
- if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll)
+ if (this.segmentIntegrityHandling is SegmentIntegrityHandling.IgnoreData or SegmentIntegrityHandling.IgnoreAll)
{
goto EXIT;
}
@@ -1927,7 +1927,7 @@ private bool TryReadChunk(Span buffer, out PngChunk chunk)
private void ValidateChunk(in PngChunk chunk, Span buffer)
{
uint inputCrc = this.ReadChunkCrc(buffer);
- if (chunk.IsCritical(this.pngCrcChunkHandling))
+ if (chunk.IsCritical(this.segmentIntegrityHandling))
{
Span chunkType = stackalloc byte[4];
BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type);
diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
index abfa4b1da8..a73db87774 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
@@ -11,11 +11,6 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions
///
public DecoderOptions GeneralOptions { get; init; } = new DecoderOptions();
- ///
- /// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
- ///
- public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical;
-
///
/// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
/// Defaults to 8MB
diff --git a/src/ImageSharp/Formats/SegmentIntegrityHandling.cs b/src/ImageSharp/Formats/SegmentIntegrityHandling.cs
new file mode 100644
index 0000000000..977aee4ad5
--- /dev/null
+++ b/src/ImageSharp/Formats/SegmentIntegrityHandling.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Specifies how to handle validation of errors in different segments of encoded image files.
+///
+public enum SegmentIntegrityHandling
+{
+ ///
+ /// Do not ignore any errors.
+ ///
+ IgnoreNone,
+
+ ///
+ /// Ignore errors in non-critical segments of the encoded image.
+ ///
+ IgnoreNonCritical,
+
+ ///
+ /// Ignore errors in data segments (e.g., image data, metadata).
+ ///
+ IgnoreData,
+
+ ///
+ /// Ignore errors in all segments.
+ ///
+ IgnoreAll
+}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 8492d78f86..9f3c5f6828 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -381,6 +381,20 @@ public void Identify(string imagePath, int expectedPixelSize)
Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
}
+ [Theory]
+ [InlineData(TestImages.Png.Bad.WrongCrcDataChunk, 1)]
+ [InlineData(TestImages.Png.Bad.Issue2589, 24)]
+ public void Identify_IgnoreCrcErrors(string imagePath, int expectedPixelSize)
+ {
+ TestFile testFile = TestFile.Create(imagePath);
+ using MemoryStream stream = new(testFile.Bytes, false);
+
+ ImageInfo imageInfo = Image.Identify(new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData }, stream);
+
+ Assert.NotNull(imageInfo);
+ Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel);
+ }
+
[Theory]
[WithFile(TestImages.Png.Bad.MissingDataChunk, PixelTypes.Rgba32)]
public void Decode_MissingDataChunk_ThrowsException(TestImageProvider provider)
@@ -479,7 +493,7 @@ public void Decode_InvalidDataChunkCrc_ThrowsException(TestImageProvider
public void Decode_InvalidDataChunkCrc_IgnoreCrcErrors(TestImageProvider provider, bool compare)
where TPixel : unmanaged, IPixel
{
- using Image image = provider.GetImage(PngDecoder.Instance, new PngDecoderOptions() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData });
+ using Image image = provider.GetImage(PngDecoder.Instance, new DecoderOptions() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData });
image.DebugSave(provider);
if (compare)
@@ -660,7 +674,7 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr)
public void Binary_PrematureEof()
{
PngDecoder decoder = PngDecoder.Instance;
- PngDecoderOptions options = new() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData };
+ PngDecoderOptions options = new() { GeneralOptions = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreData } };
using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446, decoder, options);
// TODO: Try to reduce this to 1.