Skip to content

Commit b29581e

Browse files
committed
Add support for decoding tiff's with 32bit float gray pixel data with min is white
1 parent 9d6b7a6 commit b29581e

File tree

9 files changed

+96
-1
lines changed

9 files changed

+96
-1
lines changed

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public static TiffBaseColorDecoder<TPixel> Create(Configuration configuration, T
4747
DebugGuard.IsTrue(colorMap == null, "colorMap");
4848
return new WhiteIsZero32TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
4949

50+
case TiffColorType.WhiteIsZero32Float:
51+
DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 32, "bitsPerSample");
52+
DebugGuard.IsTrue(colorMap == null, "colorMap");
53+
return new WhiteIsZero32FloatTiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
54+
5055
case TiffColorType.BlackIsZero:
5156
DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample");
5257
DebugGuard.IsTrue(colorMap == null, "colorMap");

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ internal enum TiffColorType
8383
/// </summary>
8484
WhiteIsZero32,
8585

86+
/// <summary>
87+
/// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Pixel data is 32-bit float.
88+
/// </summary>
89+
WhiteIsZero32Float,
90+
8691
/// <summary>
8792
/// Palette-color.
8893
/// </summary>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Numerics;
6+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
7+
using SixLabors.ImageSharp.Memory;
8+
using SixLabors.ImageSharp.PixelFormats;
9+
10+
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
11+
{
12+
/// <summary>
13+
/// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images.
14+
/// </summary>
15+
internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
16+
where TPixel : unmanaged, IPixel<TPixel>
17+
{
18+
private readonly bool isBigEndian;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="WhiteIsZero32FloatTiffColor{TPixel}" /> class.
22+
/// </summary>
23+
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
24+
public WhiteIsZero32FloatTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
25+
26+
/// <inheritdoc/>
27+
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
28+
{
29+
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
30+
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
31+
var color = default(TPixel);
32+
color.FromVector4(TiffUtils.Vector4Default);
33+
byte[] buffer = new byte[4];
34+
35+
int offset = 0;
36+
for (int y = top; y < top + height; y++)
37+
{
38+
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
39+
if (this.isBigEndian)
40+
{
41+
for (int x = 0; x < pixelRow.Length; x++)
42+
{
43+
data.Slice(offset, 4).CopyTo(buffer);
44+
Array.Reverse(buffer);
45+
float intensity = 1.0f - BitConverter.ToSingle(buffer, 0);
46+
offset += 4;
47+
48+
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
49+
color.FromVector4(colorVector);
50+
pixelRow[x] = color;
51+
}
52+
}
53+
else
54+
{
55+
for (int x = 0; x < pixelRow.Length; x++)
56+
{
57+
data.Slice(offset, 4).CopyTo(buffer);
58+
float intensity = 1.0f - BitConverter.ToSingle(buffer, 0);
59+
offset += 4;
60+
61+
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
62+
color.FromVector4(colorVector);
63+
pixelRow[x] = color;
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
2727
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
2828
for (int x = 0; x < pixelRow.Length; x++)
2929
{
30-
byte intensity = (byte)(255 - data[offset++]);
30+
byte intensity = (byte)(byte.MaxValue - data[offset++]);
3131
pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color);
3232
}
3333
}

src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ private static void ParseColorType(this TiffDecoderCore options, ExifProfile exi
116116
{
117117
case 32:
118118
{
119+
if (options.SampleFormat == TiffSampleFormat.Float)
120+
{
121+
options.ColorType = TiffColorType.WhiteIsZero32Float;
122+
return;
123+
}
124+
119125
options.ColorType = TiffColorType.WhiteIsZero32;
120126
break;
121127
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ public void TiffDecoder_CanDecode_Float_96Bit<TPixel>(TestImageProvider<TPixel>
251251
[Theory]
252252
[WithFile(Flower32BitFloatGray, PixelTypes.Rgba32)]
253253
[WithFile(Flower32BitFloatGrayLittleEndian, PixelTypes.Rgba32)]
254+
[WithFile(Flower32BitFloatGrayMinIsWhite, PixelTypes.Rgba32)]
255+
[WithFile(Flower32BitFloatGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)]
254256
public void TiffDecoder_CanDecode_Float_96Bit_Gray<TPixel>(TestImageProvider<TPixel> provider)
255257
where TPixel : unmanaged, IPixel<TPixel>
256258
{

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ public static class Tiff
607607
public const string Flower32BitGrayLittleEndian = "Tiff/flower-minisblack-32_lsb.tiff";
608608
public const string Flower32BitFloatGray = "Tiff/flower-minisblack-float32_msb.tiff";
609609
public const string Flower32BitFloatGrayLittleEndian = "Tiff/flower-minisblack-float32_lsb.tiff";
610+
public const string Flower32BitFloatGrayMinIsWhite = "Tiff/flower-miniswhite-float32_msb.tiff";
611+
public const string Flower32BitFloatGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-float32_lsb.tiff";
610612
public const string Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.tiff";
611613
public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff";
612614

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:93e06533486f15e33f2435d081713fbecc3ba96c842058b7ba3a5d9116fe5f5c
3+
size 12814
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:800a101f4d23fa2a499fcef036ebfca7d9338ac71b06a32ad05e7eb1905ddae3
3+
size 12814

0 commit comments

Comments
 (0)