From 8e6fad805cef36a305a332b517d5ba7a13e6a5e9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Jun 2021 12:35:30 +0200 Subject: [PATCH] Add support for decoding 30 bit per pixel tiff's --- src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs | 5 +++++ .../TiffColorDecoderFactory{TPixel}.cs | 10 ++++++++++ .../Tiff/PhotometricInterpretation/TiffColorType.cs | 5 +++++ src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs | 7 +++++++ src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs | 5 +++++ .../Formats/Tiff/TiffBitsPerSampleExtensions.cs | 9 +++++++++ .../Formats/Tiff/TiffDecoderOptionsParser.cs | 5 +++++ src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs | 3 ++- .../ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 6 ++++++ .../ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 2 ++ .../ReferenceCodecs/MagickReferenceDecoder.cs | 2 +- tests/Images/Input/Tiff/flower-rgb-contig-10.tiff | 3 +++ tests/Images/Input/Tiff/flower-rgb-planar-10.tiff | 3 +++ 14 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Tiff/flower-rgb-contig-10.tiff create mode 100644 tests/Images/Input/Tiff/flower-rgb-planar-10.tiff diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index f56488a52f..2327528b0e 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -110,6 +110,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public static readonly ushort[] BitsPerSampleRgb8Bit = { 8, 8, 8 }; + /// + /// The bits per sample for color images with 10 bits for each color channel. + /// + public static readonly ushort[] BitsPerSampleRgb10Bit = { 10, 10, 10 }; + /// /// The list of mimetypes that equate to a tiff. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index 2c59fdf137..9ebf48620a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -87,6 +87,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation DebugGuard.IsTrue(colorMap == null, "colorMap"); return new Rgb888TiffColor(); + case TiffColorType.Rgb101010: + DebugGuard.IsTrue( + bitsPerSample.Length == 3 + && bitsPerSample[0] == 10 + && bitsPerSample[1] == 10 + && bitsPerSample[2] == 10, + "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new RgbTiffColor(bitsPerSample); + case TiffColorType.PaletteColor: DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); DebugGuard.NotNull(colorMap, "colorMap"); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 1d6535fd71..afa86b1430 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -73,6 +73,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// Rgb888, + /// + /// RGB color image with 10 bits for each channel. + /// + Rgb101010, + /// /// RGB Full Color. Planar configuration of data. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 0dee1105bb..dc1ef0fd6e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -41,5 +41,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// 24 bits per pixel. One byte for each color channel. /// Bit24 = 24, + + /// + /// 30 bits per pixel. 10 bit for each color channel. + /// + /// Note: The TiffEncoder does not yet support 10 bits per color channel and will default to 24 bits per pixel instead. + /// + Bit30 = 30, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index 0a4962b530..02378ded30 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -42,5 +42,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// 24 bits per sample, each color channel has 8 Bits. /// Bit24 = 24, + + /// + /// Thirty bits per sample, each channel has 10 bits. + /// + Bit30 = 30, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs index 923e355f40..51a5a53a7a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs @@ -29,6 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff return TiffConstants.BitsPerSampleRgb4Bit; case TiffBitsPerSample.Bit24: return TiffConstants.BitsPerSampleRgb8Bit; + case TiffBitsPerSample.Bit30: + return TiffConstants.BitsPerSampleRgb10Bit; default: return Array.Empty(); @@ -45,6 +47,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerSample.Length) { case 3: + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb10Bit[2] && + bitsPerSample[1] == TiffConstants.BitsPerSampleRgb10Bit[1] && + bitsPerSample[0] == TiffConstants.BitsPerSampleRgb10Bit[0]) + { + return TiffBitsPerSample.Bit30; + } + if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2] && bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] && bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0]) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 1c2ee2443d..cf6ac6dc75 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -182,6 +182,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff { switch (options.BitsPerSample) { + case TiffBitsPerSample.Bit30: + options.ColorType = TiffColorType.Rgb101010; + break; + case TiffBitsPerSample.Bit24: options.ColorType = TiffColorType.Rgb888; break; @@ -292,6 +296,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffBitsPerPixel.Bit8 => TiffBitsPerSample.Bit8, TiffBitsPerPixel.Bit12 => TiffBitsPerSample.Bit12, TiffBitsPerPixel.Bit24 => TiffBitsPerSample.Bit24, + TiffBitsPerPixel.Bit30 => TiffBitsPerSample.Bit30, _ => throw new NotSupportedException("The bits per pixel are not supported"), }; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index b61a0c0e1a..edfa215cc5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -321,7 +321,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; case TiffBitsPerPixel.Bit6: case TiffBitsPerPixel.Bit12: - // Encoding 12 and 6 bits per pixel is not yet supported. Default to 24 bits. + case TiffBitsPerPixel.Bit30: + // Encoding 30, 12 and 6 bits per pixel is not yet supported. Default to 24 bits. this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); break; default: diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ad27ed0fcc..0dd8e0e22e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -111,6 +111,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffDecoder_CanDecode_12Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(FlowerRgb101010Contiguous, PixelTypes.Rgba32)] + [WithFile(FlowerRgb101010Planar, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_30Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 1b3104e728..19cfc42e4f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] + [InlineData(TiffBitsPerPixel.Bit30)] [InlineData(TiffBitsPerPixel.Bit12)] [InlineData(TiffBitsPerPixel.Bit6)] public void EncoderOptions_UnsupportedBitPerPixel_DefaultTo24Bits(TiffBitsPerPixel bitsPerPixel) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3eff09c75f..05045d06a4 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -560,6 +560,8 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; public const string Flower4BitPalette = "Tiff/flower-palette-04.tiff"; public const string Flower4BitPaletteGray = "Tiff/flower-minisblack-04.tiff"; + public const string FlowerRgb101010Contiguous = "Tiff/flower-rgb-contig-10.tiff"; + public const string FlowerRgb101010Planar = "Tiff/flower-rgb-planar-10.tiff"; public const string FlowerRgb444Contiguous = "Tiff/flower-rgb-contig-04.tiff"; public const string FlowerRgb444Planar = "Tiff/flower-rgb-planar-04.tiff"; public const string FlowerRgb222Contiguous = "Tiff/flower-rgb-contig-02.tiff"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 885d12e774..30c2214b5a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe(); - if (magicFrame.Depth == 8 || magicFrame.Depth == 4 || magicFrame.Depth == 2 || magicFrame.Depth == 1) + if (magicFrame.Depth == 8 || magicFrame.Depth == 4 || magicFrame.Depth == 2 || magicFrame.Depth == 1 || magicFrame.Depth == 10) { byte[] data = pixels.ToByteArray(PixelMapping.RGBA); diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-10.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-10.tiff new file mode 100644 index 0000000000..2b271c8004 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-contig-10.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68168ea1c2e50e674a7c5c41e5b055c881adf8cb940d0fd033a927a7ebdd7b6f +size 12117 diff --git a/tests/Images/Input/Tiff/flower-rgb-planar-10.tiff b/tests/Images/Input/Tiff/flower-rgb-planar-10.tiff new file mode 100644 index 0000000000..be0acd6465 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-planar-10.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f53948d4a36c80f45d70a315d2e76514ec41cabe982c06dbbd0d47e671120e2 +size 12211