From 07cc9f79a58a1c93a9f70bd40cafede82f96f206 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 11 Jun 2025 14:03:56 +1000 Subject: [PATCH] Allow additional and undefined extra samples --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 20 ++++++++++--------- .../Formats/Tiff/TiffExtraSampleType.cs | 8 +++++++- .../Formats/Tiff/TiffDecoderTests.cs | 5 +++++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../Tiff/Issues/ExtraSamplesUnspecified.tif | 3 +++ 5 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index ed9175546..7519871b7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -25,20 +25,22 @@ internal static class TiffDecoderOptionsParser /// True, if the image uses tiles. Otherwise the images has strip's. public static bool VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { - IExifValue extraSamplesExifValue = exifProfile.GetValueInternal(ExifTag.ExtraSamples); - if (extraSamplesExifValue is not null) + if (exifProfile.TryGetValue(ExifTag.ExtraSamples, out IExifValue samples)) { - short[] extraSamples = (short[])extraSamplesExifValue.GetValue(); - if (extraSamples.Length != 1) + // We only support a single sample pertaining to alpha data. + // Other information is discarded. + TiffExtraSampleType sampleType = (TiffExtraSampleType)samples.Value[0]; + if (sampleType is TiffExtraSampleType.CorelDrawUnassociatedAlphaData) { - TiffThrowHelper.ThrowNotSupported("ExtraSamples is only supported with one extra sample for alpha data."); + // According to libtiff, this CorelDRAW-specific value indicates unassociated alpha. + // Patch required for compatibility with malformed CorelDRAW-generated TIFFs. + // https://libtiff.gitlab.io/libtiff/releases/v3.9.0beta.html + sampleType = TiffExtraSampleType.UnassociatedAlphaData; } - TiffExtraSampleType extraSamplesType = (TiffExtraSampleType)extraSamples[0]; - options.ExtraSamplesType = extraSamplesType; - if (extraSamplesType is not (TiffExtraSampleType.UnassociatedAlphaData or TiffExtraSampleType.AssociatedAlphaData)) + if (sampleType is (TiffExtraSampleType.UnassociatedAlphaData or TiffExtraSampleType.AssociatedAlphaData)) { - TiffThrowHelper.ThrowNotSupported("Decoding Tiff images with ExtraSamples is not supported with UnspecifiedData."); + options.ExtraSamplesType = sampleType; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs b/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs index 484fc993b..cb7582764 100644 --- a/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs +++ b/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs @@ -22,5 +22,11 @@ internal enum TiffExtraSampleType /// The extra data is unassociated alpha data is transparency information that logically exists independent of an image; /// it is commonly called a soft matte. /// - UnassociatedAlphaData = 2 + UnassociatedAlphaData = 2, + + /// + /// A CorelDRAW-specific value observed in damaged files, indicating unassociated alpha. + /// Not part of the official TIFF specification; patched in ImageSharp for compatibility. + /// + CorelDrawUnassociatedAlphaData = 999, } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index cf27e561f..a16b26f9e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -828,4 +828,9 @@ public class TiffDecoderTests : TiffDecoderBaseTester testOutputDetails: details, appendPixelTypeToFileName: false); } + + [Theory] + [WithFile(ExtraSamplesUnspecified, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_ExtraSamplesUnspecified(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 0908e6e6b..9b73e60cf 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1136,6 +1136,7 @@ public static class TestImages public const string Issues2679 = "Tiff/Issues/Issue2679.tiff"; public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff"; public const string Tiled0000023664 = "Tiff/Issues/tiled-0000023664.tiff"; + public const string ExtraSamplesUnspecified = "Tiff/Issues/ExtraSamplesUnspecified.tif"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; diff --git a/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif b/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif new file mode 100644 index 000000000..b5835c1ed --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71d28f17d2d56481faa4d241fae882eae4f8303c70f85bc3759f6a0c2074979e +size 1426558