Skip to content

Commit c975a1f

Browse files
Merge branch 'main' into lvk/metadata-debuggerdisplay
2 parents 1e4b895 + 98437a0 commit c975a1f

File tree

12 files changed

+94
-49
lines changed

12 files changed

+94
-49
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+
}

src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,6 @@ private static void ParseCompression(this TiffDecoderCore options, TiffCompressi
572572
}
573573

574574
options.CompressionType = TiffDecoderCompressionType.OldJpeg;
575-
576575
if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr)
577576
{
578577
// Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data.
@@ -585,6 +584,14 @@ private static void ParseCompression(this TiffDecoderCore options, TiffCompressi
585584
case TiffCompression.Jpeg:
586585
options.CompressionType = TiffDecoderCompressionType.Jpeg;
587586

587+
// Some tiff encoder set this to values different from [1, 1]. The jpeg decoder already handles this,
588+
// so we set this always to [1, 1], see: https://github.com/SixLabors/ImageSharp/issues/2679
589+
if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.YcbcrSubSampling != null)
590+
{
591+
options.YcbcrSubSampling[0] = 1;
592+
options.YcbcrSubSampling[1] = 1;
593+
}
594+
588595
if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null)
589596
{
590597
// Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data.

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.

tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,23 @@ public void TiffDecoder_CanDecode_TiledWithNonEqualWidthAndHeight<TPixel>(TestIm
671671
public void TiffDecoder_CanDecode_BiColorWithMissingBitsPerSample<TPixel>(TestImageProvider<TPixel> provider)
672672
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
673673

674+
// https://github.com/SixLabors/ImageSharp/issues/2679
675+
[Theory]
676+
[WithFile(Issues2679, PixelTypes.Rgba32)]
677+
public void TiffDecoder_CanDecode_JpegCompressedWithIssue2679<TPixel>(TestImageProvider<TPixel> provider)
678+
where TPixel : unmanaged, IPixel<TPixel>
679+
{
680+
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
681+
682+
// The image is handcrafted to simulate issue 2679. ImageMagick will throw an expection here and wont decode,
683+
// so we compare to rererence output instead.
684+
image.DebugSave(provider);
685+
image.CompareToReferenceOutput(
686+
ImageComparer.Exact,
687+
provider,
688+
appendPixelTypeToFileName: false);
689+
}
690+
674691
[Theory]
675692
[WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)]
676693
public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets<TPixel>(TestImageProvider<TPixel> provider)

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ public static class Tiff
10471047
public const string Issues2255 = "Tiff/Issues/Issue2255.png";
10481048
public const string Issues2435 = "Tiff/Issues/Issue2435.tiff";
10491049
public const string Issues2587 = "Tiff/Issues/Issue2587.tiff";
1050+
public const string Issues2679 = "Tiff/Issues/Issue2679.tiff";
10501051
public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff";
10511052
public const string Tiled0000023664 = "Tiff/Issues/tiled-0000023664.tiff";
10521053

0 commit comments

Comments
 (0)