Browse Source

Add support for decoding tiff's with 32bit float gray pixel data with min is white

pull/1727/head
Brian Popow 5 years ago
parent
commit
b29581e57a
  1. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
  2. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
  3. 69
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
  4. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs
  5. 6
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  6. 2
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  7. 2
      tests/ImageSharp.Tests/TestImages.cs
  8. 3
      tests/Images/Input/Tiff/flower-miniswhite-float32_lsb.tiff
  9. 3
      tests/Images/Input/Tiff/flower-miniswhite-float32_msb.tiff

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

@ -47,6 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap"); DebugGuard.IsTrue(colorMap == null, "colorMap");
return new WhiteIsZero32TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian); return new WhiteIsZero32TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
case TiffColorType.WhiteIsZero32Float:
DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 32, "bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new WhiteIsZero32FloatTiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);
case TiffColorType.BlackIsZero: case TiffColorType.BlackIsZero:
DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample"); DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap"); DebugGuard.IsTrue(colorMap == null, "colorMap");

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

@ -83,6 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// </summary> /// </summary>
WhiteIsZero32, WhiteIsZero32,
/// <summary>
/// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Pixel data is 32-bit float.
/// </summary>
WhiteIsZero32Float,
/// <summary> /// <summary>
/// Palette-color. /// Palette-color.
/// </summary> /// </summary>

69
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
{
/// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images.
/// </summary>
internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly bool isBigEndian;
/// <summary>
/// Initializes a new instance of the <see cref="WhiteIsZero32FloatTiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public WhiteIsZero32FloatTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> 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<TPixel> 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;
}
}
}
}
}
}

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

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width); Span<TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++) 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); pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color);
} }
} }

6
src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

@ -116,6 +116,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{ {
case 32: case 32:
{ {
if (options.SampleFormat == TiffSampleFormat.Float)
{
options.ColorType = TiffColorType.WhiteIsZero32Float;
return;
}
options.ColorType = TiffColorType.WhiteIsZero32; options.ColorType = TiffColorType.WhiteIsZero32;
break; break;
} }

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

@ -251,6 +251,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory] [Theory]
[WithFile(Flower32BitFloatGray, PixelTypes.Rgba32)] [WithFile(Flower32BitFloatGray, PixelTypes.Rgba32)]
[WithFile(Flower32BitFloatGrayLittleEndian, PixelTypes.Rgba32)] [WithFile(Flower32BitFloatGrayLittleEndian, PixelTypes.Rgba32)]
[WithFile(Flower32BitFloatGrayMinIsWhite, PixelTypes.Rgba32)]
[WithFile(Flower32BitFloatGrayMinIsWhiteLittleEndian, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_Float_96Bit_Gray<TPixel>(TestImageProvider<TPixel> provider) public void TiffDecoder_CanDecode_Float_96Bit_Gray<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

2
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 Flower32BitGrayLittleEndian = "Tiff/flower-minisblack-32_lsb.tiff";
public const string Flower32BitFloatGray = "Tiff/flower-minisblack-float32_msb.tiff"; public const string Flower32BitFloatGray = "Tiff/flower-minisblack-float32_msb.tiff";
public const string Flower32BitFloatGrayLittleEndian = "Tiff/flower-minisblack-float32_lsb.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 Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.tiff";
public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff"; public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff";

3
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

3
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
Loading…
Cancel
Save