diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
new file mode 100644
index 000000000..739d1059c
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
@@ -0,0 +1,89 @@
+// 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 'RGB' photometric interpretation with 32 bits for each channel.
+ ///
+ internal class RgbFloat323232TiffColor : 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 RgbFloat323232TiffColor(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);
+ int offset = 0;
+ byte[] buffer = new byte[4];
+
+ 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 r = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float g = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float b = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ var colorVector = new Vector4(r, g, b, 1.0f);
+ Array.Reverse(buffer);
+ color.FromVector4(colorVector);
+ pixelRow[x] = color;
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 4).CopyTo(buffer);
+ float r = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ float g = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ float b = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ var colorVector = new Vector4(r, g, b, 1.0f);
+ color.FromVector4(colorVector);
+ pixelRow[x] = color;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index 4a2fe93fe..e1c813027 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -176,6 +176,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.RgbFloat323232:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 32
+ && bitsPerSample.Channel1 == 32
+ && bitsPerSample.Channel0 == 32,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbFloat323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.PaletteColor:
DebugGuard.NotNull(colorMap, "colorMap");
return new PaletteTiffColor(bitsPerSample, colorMap);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
index dc47dc8cd..07959056c 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -133,6 +133,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
Rgb323232,
+ ///
+ /// RGB color image with 32 bits floats for each channel.
+ ///
+ RgbFloat323232,
+
///
/// RGB Full Color. Planar configuration of data. 8 Bit per color channel.
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 63185619d..cb5b82bfd 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -95,6 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
public TiffPhotometricInterpretation PhotometricInterpretation { get; set; }
+ ///
+ /// Gets or sets the sample format.
+ ///
+ public TiffSampleFormat SampleFormat { get; set; }
+
///
/// Gets or sets the horizontal predictor.
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index 8590203a7..8449eb7b2 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -45,14 +45,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported.");
}
- TiffSampleFormat[] sampleFormat = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
- if (sampleFormat != null)
+ TiffSampleFormat[] sampleFormats = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
+ TiffSampleFormat? sampleFormat = null;
+ if (sampleFormats != null)
{
- foreach (TiffSampleFormat format in sampleFormat)
+ sampleFormat = sampleFormats[0];
+ foreach (TiffSampleFormat format in sampleFormats)
{
- if (format != TiffSampleFormat.UnsignedInteger)
+ if (format != TiffSampleFormat.UnsignedInteger && format != TiffSampleFormat.Float)
{
- TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger SampleFormat.");
+ TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger and Float SampleFormat.");
}
}
}
@@ -67,6 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None;
options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb;
+ options.SampleFormat = sampleFormat ?? TiffSampleFormat.UnsignedInteger;
options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0);
@@ -231,6 +234,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffThrowHelper.ThrowNotSupported("Only BitsPerSample with equal bits per channel are supported.");
}
+ if (options.SampleFormat == TiffSampleFormat.Float)
+ {
+ options.ColorType = TiffColorType.RgbFloat323232;
+ return;
+ }
+
if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
ushort bitsPerChannel = options.BitsPerSample.Channel0;
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 182fafa33..983935038 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -237,6 +237,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.DebugSave(provider);
}
+ [Theory]
+ [WithFile(FlowerRgbFloat323232, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgbFloat323232LittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_Float_96Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ // Note: because the MagickReferenceDecoder fails to load the image, we only debug save them.
+ using Image image = provider.GetImage();
+ image.DebugSave(provider);
+ }
+
[Theory]
[WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)]
[WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)]
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 8eaaec493..74fde6a56 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -563,6 +563,8 @@ namespace SixLabors.ImageSharp.Tests
public const string RgbPalette = "Tiff/rgb_palette.tiff";
public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff";
public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff";
+ public const string FlowerRgbFloat323232 = "Tiff/flower-rgb-float32_msb.tiff";
+ public const string FlowerRgbFloat323232LittleEndian = "Tiff/flower-rgb-float32_lsb.tiff";
public const string FlowerRgb323232Contiguous = "Tiff/flower-rgb-contig-32.tiff";
public const string FlowerRgb323232ContiguousLittleEndian = "Tiff/flower-rgb-contig-32_lsb.tiff";
public const string FlowerRgb323232Planar = "Tiff/flower-rgb-planar-32.tiff";
diff --git a/tests/Images/Input/Tiff/flower-rgb-float32_lsb.tiff b/tests/Images/Input/Tiff/flower-rgb-float32_lsb.tiff
new file mode 100644
index 000000000..da0f10438
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-float32_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1f889baa6f3bb99f15609848cdd47d548d3e2ed1b7b558d428550dfa3bd4bf9
+size 38050
diff --git a/tests/Images/Input/Tiff/flower-rgb-float32_msb.tiff b/tests/Images/Input/Tiff/flower-rgb-float32_msb.tiff
new file mode 100644
index 000000000..353771db9
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-float32_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c02be5dff2bcd5d60afbf379ba9095b0c8fd3a7a0063f684ac9ac9119f967a5
+size 38050