From b29581e57a759d7fa2dcff7a483a28f8d3a78f3d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 6 Aug 2021 20:54:32 +0200 Subject: [PATCH] Add support for decoding tiff's with 32bit float gray pixel data with min is white --- .../TiffColorDecoderFactory{TPixel}.cs | 5 ++ .../TiffColorType.cs | 5 ++ .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 69 +++++++++++++++++++ .../WhiteIsZero8TiffColor{TPixel}.cs | 2 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 6 ++ .../Formats/Tiff/TiffDecoderTests.cs | 2 + tests/ImageSharp.Tests/TestImages.cs | 2 + .../Tiff/flower-miniswhite-float32_lsb.tiff | 3 + .../Tiff/flower-miniswhite-float32_msb.tiff | 3 + 9 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs create mode 100644 tests/Images/Input/Tiff/flower-miniswhite-float32_lsb.tiff create mode 100644 tests/Images/Input/Tiff/flower-miniswhite-float32_msb.tiff diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index f3cc4c01c..e27c0a61c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -47,6 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZero32TiffColor(byteOrder == ByteOrder.BigEndian); + case TiffColorType.WhiteIsZero32Float: + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 32, "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new WhiteIsZero32FloatTiffColor(byteOrder == ByteOrder.BigEndian); + case TiffColorType.BlackIsZero: DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 0d742c2b9..81db744a1 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -83,6 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// WhiteIsZero32, + /// + /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Pixel data is 32-bit float. + /// + WhiteIsZero32Float, + /// /// Palette-color. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs new file mode 100644 index 000000000..d532247fe --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation +{ + /// + /// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images. + /// + internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder + where TPixel : unmanaged, IPixel + { + private readonly bool isBigEndian; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true decodes the pixel data as big endian, otherwise as little endian. + public WhiteIsZero32FloatTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; + + /// + public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) + { + // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, + // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 + var color = default(TPixel); + color.FromVector4(TiffUtils.Vector4Default); + byte[] buffer = new byte[4]; + + int offset = 0; + for (int y = top; y < top + height; y++) + { + Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); + if (this.isBigEndian) + { + for (int x = 0; x < pixelRow.Length; x++) + { + data.Slice(offset, 4).CopyTo(buffer); + Array.Reverse(buffer); + float intensity = 1.0f - BitConverter.ToSingle(buffer, 0); + offset += 4; + + var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); + color.FromVector4(colorVector); + pixelRow[x] = color; + } + } + else + { + for (int x = 0; x < pixelRow.Length; x++) + { + data.Slice(offset, 4).CopyTo(buffer); + float intensity = 1.0f - BitConverter.ToSingle(buffer, 0); + offset += 4; + + var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); + color.FromVector4(colorVector); + pixelRow[x] = color; + } + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs index 6a6c2af22..15ebed58f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation Span pixelRow = pixels.GetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { - byte intensity = (byte)(255 - data[offset++]); + byte intensity = (byte)(byte.MaxValue - data[offset++]); pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 79c4d372d..5fa28f418 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -116,6 +116,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff { case 32: { + if (options.SampleFormat == TiffSampleFormat.Float) + { + options.ColorType = TiffColorType.WhiteIsZero32Float; + return; + } + options.ColorType = TiffColorType.WhiteIsZero32; break; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index e45b994a6..350b08916 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -251,6 +251,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Flower32BitFloatGray, PixelTypes.Rgba32)] [WithFile(Flower32BitFloatGrayLittleEndian, PixelTypes.Rgba32)] + [WithFile(Flower32BitFloatGrayMinIsWhite, PixelTypes.Rgba32)] + [WithFile(Flower32BitFloatGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Float_96Bit_Gray(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index eb83062d9..f1c852da3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -607,6 +607,8 @@ namespace SixLabors.ImageSharp.Tests public const string Flower32BitGrayLittleEndian = "Tiff/flower-minisblack-32_lsb.tiff"; public const string Flower32BitFloatGray = "Tiff/flower-minisblack-float32_msb.tiff"; public const string Flower32BitFloatGrayLittleEndian = "Tiff/flower-minisblack-float32_lsb.tiff"; + public const string Flower32BitFloatGrayMinIsWhite = "Tiff/flower-miniswhite-float32_msb.tiff"; + public const string Flower32BitFloatGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-float32_lsb.tiff"; public const string Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.tiff"; public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff"; diff --git a/tests/Images/Input/Tiff/flower-miniswhite-float32_lsb.tiff b/tests/Images/Input/Tiff/flower-miniswhite-float32_lsb.tiff new file mode 100644 index 000000000..3241c8fbe --- /dev/null +++ b/tests/Images/Input/Tiff/flower-miniswhite-float32_lsb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93e06533486f15e33f2435d081713fbecc3ba96c842058b7ba3a5d9116fe5f5c +size 12814 diff --git a/tests/Images/Input/Tiff/flower-miniswhite-float32_msb.tiff b/tests/Images/Input/Tiff/flower-miniswhite-float32_msb.tiff new file mode 100644 index 000000000..5623a7428 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-miniswhite-float32_msb.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:800a101f4d23fa2a499fcef036ebfca7d9338ac71b06a32ad05e7eb1905ddae3 +size 12814