diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
index 2188913bc3..bb57853d53 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
@@ -5,8 +5,8 @@ using System;
using System.IO.Compression;
using SixLabors.ImageSharp.Compression.Zlib;
-using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -20,16 +20,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
///
internal class DeflateTiffCompression : TiffBaseDecompressor
{
+ private readonly bool isBigEndian;
+
+ private readonly TiffColorType colorType;
+
///
/// Initializes a new instance of the class.
///
/// The memoryAllocator to use for buffer allocations.
/// The image width.
/// The bits used per pixel.
+ /// The color type of the pixel data.
/// The tiff predictor used.
- public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor)
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
+ this.colorType = colorType;
+ this.isBigEndian = isBigEndian;
}
///
@@ -62,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
- HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel);
+ HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
index 7e75dd4f05..2e89396075 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
@@ -3,6 +3,7 @@
using System;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -13,16 +14,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
///
internal class LzwTiffCompression : TiffBaseDecompressor
{
+ private readonly bool isBigEndian;
+
+ private readonly TiffColorType colorType;
+
///
/// Initializes a new instance of the class.
///
/// The memoryAllocator to use for buffer allocations.
/// The image width.
/// The bits used per pixel.
+ /// The color type of the pixel data.
/// The tiff predictor used.
- public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor)
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
+ this.colorType = colorType;
+ this.isBigEndian = isBigEndian;
}
///
@@ -33,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
- HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel);
+ HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
index ae2f17dbb5..e2dbc6ca99 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
@@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
@@ -19,16 +21,34 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
///
/// Buffer with decompressed pixel data.
/// The width of the image or strip.
- /// Bits per pixel.
- public static void Undo(Span pixelBytes, int width, int bitsPerPixel)
+ /// The color type of the pixel data.
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public static void Undo(Span pixelBytes, int width, TiffColorType colorType, bool isBigEndian)
{
- if (bitsPerPixel == 8)
- {
- Undo8Bit(pixelBytes, width);
- }
- else if (bitsPerPixel == 24)
+ switch (colorType)
{
- Undo24Bit(pixelBytes, width);
+ case TiffColorType.BlackIsZero8:
+ case TiffColorType.WhiteIsZero8:
+ case TiffColorType.PaletteColor:
+ UndoGray8Bit(pixelBytes, width);
+ break;
+ case TiffColorType.BlackIsZero16:
+ case TiffColorType.WhiteIsZero16:
+ UndoGray16Bit(pixelBytes, width, isBigEndian);
+ break;
+ case TiffColorType.BlackIsZero32:
+ case TiffColorType.WhiteIsZero32:
+ UndoGray32Bit(pixelBytes, width, isBigEndian);
+ break;
+ case TiffColorType.Rgb888:
+ UndoRgb24Bit(pixelBytes, width);
+ break;
+ case TiffColorType.Rgb161616:
+ UndoRgb48Bit(pixelBytes, width, isBigEndian);
+ break;
+ case TiffColorType.Rgb323232:
+ UndoRgb96Bit(pixelBytes, width, isBigEndian);
+ break;
}
}
@@ -93,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
- private static void Undo8Bit(Span pixelBytes, int width)
+ private static void UndoGray8Bit(Span pixelBytes, int width)
{
int rowBytesCount = width;
int height = pixelBytes.Length / rowBytesCount;
@@ -110,7 +130,95 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
- private static void Undo24Bit(Span pixelBytes, int width)
+ private static void UndoGray16Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 2;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort pixelValue = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort diff = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ pixelValue += diff;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue);
+ offset += 2;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort pixelValue = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort diff = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ pixelValue += diff;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue);
+ offset += 2;
+ }
+ }
+ }
+ }
+
+ private static void UndoGray32Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 4;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint pixelValue = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint diff = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ pixelValue += diff;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, pixelValue);
+ offset += 4;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint pixelValue = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint diff = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ pixelValue += diff;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, pixelValue);
+ offset += 4;
+ }
+ }
+ }
+ }
+
+ private static void UndoRgb24Bit(Span pixelBytes, int width)
{
int rowBytesCount = width * 3;
int height = pixelBytes.Length / rowBytesCount;
@@ -134,5 +242,157 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
}
+
+ private static void UndoRgb48Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 6;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b);
+ offset += 2;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b);
+ offset += 2;
+ }
+ }
+ }
+ }
+
+ private static void UndoRgb96Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 12;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b);
+ offset += 4;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b);
+ offset += 4;
+ }
+ }
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
index ff04edab70..b1562223aa 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
@@ -3,6 +3,7 @@
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
@@ -15,9 +16,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
TiffPhotometricInterpretation photometricInterpretation,
int width,
int bitsPerPixel,
+ TiffColorType colorType,
TiffPredictor predictor,
FaxCompressionOptions faxOptions,
- TiffFillOrder fillOrder)
+ TiffFillOrder fillOrder,
+ ByteOrder byteOrder)
{
switch (method)
{
@@ -33,11 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
case TiffDecoderCompressionType.Deflate:
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");
- return new DeflateTiffCompression(allocator, width, bitsPerPixel, predictor);
+ return new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian);
case TiffDecoderCompressionType.Lzw:
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");
- return new LzwTiffCompression(allocator, width, bitsPerPixel, predictor);
+ return new LzwTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian);
case TiffDecoderCompressionType.T4:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..ff34a29eb2
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{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 'BlackIsZero' photometric interpretation for 32-bit float grayscale images.
+ ///
+ internal class BlackIsZero32FloatTiffColor : 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 BlackIsZero32FloatTiffColor(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 = 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 = 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/BlackIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
index 862756bc42..f54a794840 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
@@ -2,7 +2,6 @@
// 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;
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 0000000000..f3f27d5c4b
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
@@ -0,0 +1,88 @@
+// 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);
+ 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 4a2fe93fe4..e27c0a61cf 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");
@@ -82,6 +87,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new BlackIsZero32TiffColor(byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.BlackIsZero32Float:
+ DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 32, "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new BlackIsZero32FloatTiffColor(byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.Rgb:
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
@@ -176,6 +186,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 dc47dc8cd4..81db744a1e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -43,6 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
BlackIsZero32,
+ ///
+ /// Grayscale: 0 is imaged as black. The maximum value is imaged as white. Pixel data is 32-bit float.
+ ///
+ BlackIsZero32Float,
+
///
/// Grayscale: 0 is imaged as white. The maximum value is imaged as black.
///
@@ -78,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.
///
@@ -133,6 +143,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/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
index b1088732ba..10182f250f 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
@@ -31,6 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
color.FromVector4(TiffUtils.Vector4Default);
byte[] buffer = new byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
+ const uint maxValue = 0xFFFFFF;
Span bufferSpan = buffer.AsSpan(bufferStartIdx);
int offset = 0;
@@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
for (int x = 0; x < pixelRow.Length; x++)
{
data.Slice(offset, 3).CopyTo(bufferSpan);
- ulong intensity = TiffUtils.ConvertToUIntBigEndian(buffer);
+ ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(buffer);
offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color);
@@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
for (int x = 0; x < pixelRow.Length; x++)
{
data.Slice(offset, 3).CopyTo(bufferSpan);
- ulong intensity = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(buffer);
offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, 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 0000000000..d532247fe3
--- /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/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
index 0071740036..ef62b4f441 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
@@ -29,6 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);
+ const uint maxValue = 0xFFFFFFFF;
int offset = 0;
for (int y = top; y < top + height; y++)
@@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong intensity = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
+ ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color);
@@ -48,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong intensity = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
+ ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs
index 6a6c2af225..15ebed58f9 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/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 8477ba2d20..ce8e58798e 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -100,6 +100,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.
///
@@ -275,9 +280,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
+ this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
- this.FillOrder);
+ this.FillOrder,
+ this.byteOrder);
TiffBasePlanarColorDecoder colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder);
@@ -326,9 +333,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
+ this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
- this.FillOrder);
+ this.FillOrder,
+ this.byteOrder);
TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.Configuration, this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder);
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index f59a9fbbab..a493c44678 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);
options.FillOrder = fillOrder;
@@ -114,6 +117,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
case 32:
{
+ if (options.SampleFormat == TiffSampleFormat.Float)
+ {
+ options.ColorType = TiffColorType.WhiteIsZero32Float;
+ return;
+ }
+
options.ColorType = TiffColorType.WhiteIsZero32;
break;
}
@@ -175,6 +184,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
case 32:
{
+ if (options.SampleFormat == TiffSampleFormat.Float)
+ {
+ options.ColorType = TiffColorType.BlackIsZero32Float;
+ return;
+ }
+
options.ColorType = TiffColorType.BlackIsZero32;
break;
}
@@ -238,6 +253,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (bitsPerChannel)
{
case 32:
+ if (options.SampleFormat == TiffSampleFormat.Float)
+ {
+ options.ColorType = TiffColorType.RgbFloat323232;
+ return;
+ }
+
options.ColorType = TiffColorType.Rgb323232;
break;
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
index 782a504d58..c93a2018df 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
@@ -5,6 +5,7 @@ using System.IO;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using Xunit;
@@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
{
var buffer = new byte[data.Length];
- using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None);
+ using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false);
decompressor.Decompress(stream, 0, (uint)stream.Length, buffer);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
index bf585e9c8f..5ea75d9a84 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
@@ -5,6 +5,7 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using Xunit;
@@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
using BufferedReadStream stream = CreateCompressedStream(data);
var buffer = new byte[data.Length];
- using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffPredictor.None);
+ using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false);
decompressor.Decompress(stream, 0, (uint)stream.Length, buffer);
Assert.Equal(data, buffer);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 77a8153c50..176f8c1ae6 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -161,6 +161,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_16Bit_Gray(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Flower16BitGrayPredictorBigEndian, PixelTypes.Rgba32)]
+ [WithFile(Flower16BitGrayPredictorLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_16Bit_Gray_WithPredictor(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
[Theory]
[WithFile(Flower24BitGray, PixelTypes.Rgba32)]
[WithFile(Flower24BitGrayLittleEndian, PixelTypes.Rgba32)]
@@ -191,6 +197,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.DebugSave(provider);
}
+ [Theory]
+ [WithFile(Flower32BitGrayPredictorBigEndian, PixelTypes.Rgba32)]
+ [WithFile(Flower32BitGrayPredictorLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_32Bit_Gray_WithPredictor(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(FlowerRgb121212Contiguous, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_36Bit(TestImageProvider provider)
@@ -211,6 +228,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_48Bit(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(FlowerRgb161616PredictorBigEndian, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb161616PredictorLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_48Bit_WithPredictor(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
[Theory]
[WithFile(FlowerRgb242424Contiguous, PixelTypes.Rgba32)]
[WithFile(FlowerRgb242424ContiguousLittleEndian, PixelTypes.Rgba32)]
@@ -237,6 +260,41 @@ 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(FlowerRgb323232PredictorBigEndian, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb323232PredictorLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_96Bit_WithPredictor(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(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
+ {
+ // 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 8b9aa1a5ff..9e7d44682d 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -566,16 +566,22 @@ 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";
public const string FlowerRgb323232PlanarLittleEndian = "Tiff/flower-rgb-planar-32_lsb.tiff";
+ public const string FlowerRgb323232PredictorBigEndian = "Tiff/flower-rgb-contig-32_msb_zip_predictor.tiff";
+ public const string FlowerRgb323232PredictorLittleEndian = "Tiff/flower-rgb-contig-32_lsb_zip_predictor.tiff";
public const string FlowerRgb242424Planar = "Tiff/flower-rgb-planar-24.tiff";
public const string FlowerRgb242424PlanarLittleEndian = "Tiff/flower-rgb-planar-24_lsb.tiff";
public const string FlowerRgb242424Contiguous = "Tiff/flower-rgb-contig-24.tiff";
public const string FlowerRgb242424ContiguousLittleEndian = "Tiff/flower-rgb-contig-24_lsb.tiff";
public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff";
public const string FlowerRgb161616ContiguousLittleEndian = "Tiff/flower-rgb-contig-16_lsb.tiff";
+ public const string FlowerRgb161616PredictorBigEndian = "Tiff/flower-rgb-contig-16_msb_zip_predictor.tiff";
+ public const string FlowerRgb161616PredictorLittleEndian = "Tiff/flower-rgb-contig-16_lsb_zip_predictor.tiff";
public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff";
public const string FlowerRgb161616PlanarLittleEndian = "Tiff/flower-rgb-planar-16_lsb.tiff";
public const string FlowerRgb141414Contiguous = "Tiff/flower-rgb-contig-14.tiff";
@@ -601,13 +607,21 @@ namespace SixLabors.ImageSharp.Tests
public const string Flower16BitGray = "Tiff/flower-minisblack-16.tiff";
public const string Flower16BitGrayLittleEndian = "Tiff/flower-minisblack-16_lsb.tiff";
public const string Flower16BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-16_lsb.tiff";
- public const string Flower16BitGrayMinIsWhiteBigEndian = "Tiff/flower-miniswhite-16.tiff";
- public const string Flower24BitGray = "Tiff/flower-minisblack-24.tiff";
+ public const string Flower16BitGrayPredictorBigEndian = "Tiff/flower-minisblack-16_msb_lzw_predictor.tiff";
+ public const string Flower16BitGrayPredictorLittleEndian = "Tiff/flower-minisblack-16_lsb_lzw_predictor.tiff";
+ public const string Flower16BitGrayMinIsWhiteBigEndian = "Tiff/flower-miniswhite-16_msb.tiff";
+ public const string Flower24BitGray = "Tiff/flower-minisblack-24_msb.tiff";
public const string Flower24BitGrayLittleEndian = "Tiff/flower-minisblack-24_lsb.tiff";
- public const string Flower32BitGray = "Tiff/flower-minisblack-32.tiff";
+ public const string Flower32BitGray = "Tiff/flower-minisblack-32_msb.tiff";
public const string Flower32BitGrayLittleEndian = "Tiff/flower-minisblack-32_lsb.tiff";
- public const string Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.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_msb.tiff";
public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff";
+ public const string Flower32BitGrayPredictorBigEndian = "Tiff/flower-minisblack-32_msb_deflate_predictor.tiff";
+ public const string Flower32BitGrayPredictorLittleEndian = "Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff";
public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff";
diff --git a/tests/Images/Input/Tiff/flower-minisblack-16_lsb_lzw_predictor.tiff b/tests/Images/Input/Tiff/flower-minisblack-16_lsb_lzw_predictor.tiff
new file mode 100644
index 0000000000..e28ccda483
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-16_lsb_lzw_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:263da6b84862af1bc23f2631e648b1a629b5571b253c82f3ea64f44e9b7b1fe6
+size 8478
diff --git a/tests/Images/Input/Tiff/flower-minisblack-16_msb_lzw_predictor.tiff b/tests/Images/Input/Tiff/flower-minisblack-16_msb_lzw_predictor.tiff
new file mode 100644
index 0000000000..144f96887b
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-16_msb_lzw_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e67ac19cbdeb8585b204ee958edc7679a92c2b415a1a2c6051f14fe2966f933c
+size 8504
diff --git a/tests/Images/Input/Tiff/flower-minisblack-24.tiff b/tests/Images/Input/Tiff/flower-minisblack-24_msb.tiff
similarity index 100%
rename from tests/Images/Input/Tiff/flower-minisblack-24.tiff
rename to tests/Images/Input/Tiff/flower-minisblack-24_msb.tiff
diff --git a/tests/Images/Input/Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff b/tests/Images/Input/Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff
new file mode 100644
index 0000000000..2dc9e8092a
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5e7998cc985ef11ab9da410f18dcfb6b9a3169fb1ec01f9e61aa38d8ee4cfb6
+size 12704
diff --git a/tests/Images/Input/Tiff/flower-minisblack-32.tiff b/tests/Images/Input/Tiff/flower-minisblack-32_msb.tiff
similarity index 100%
rename from tests/Images/Input/Tiff/flower-minisblack-32.tiff
rename to tests/Images/Input/Tiff/flower-minisblack-32_msb.tiff
diff --git a/tests/Images/Input/Tiff/flower-minisblack-32_msb_deflate_predictor.tiff b/tests/Images/Input/Tiff/flower-minisblack-32_msb_deflate_predictor.tiff
new file mode 100644
index 0000000000..f87c74c721
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-32_msb_deflate_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:630e7f46655b6e61c4de7d56946a3a9225db68f776f9062ff2d5372547cc7c02
+size 12704
diff --git a/tests/Images/Input/Tiff/flower-minisblack-float32_lsb.tiff b/tests/Images/Input/Tiff/flower-minisblack-float32_lsb.tiff
new file mode 100644
index 0000000000..8e94faea0b
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-float32_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af82e07e6298082c91d60a97acb29b67ecabf386bc14371fcb698b248cfd3b40
+size 12814
diff --git a/tests/Images/Input/Tiff/flower-minisblack-float32_msb.tiff b/tests/Images/Input/Tiff/flower-minisblack-float32_msb.tiff
new file mode 100644
index 0000000000..464074c153
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-minisblack-float32_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c918b8aa9968c03c12d85b3bcacd35ae57663a19f5490fc1c351521ed835f30
+size 12814
diff --git a/tests/Images/Input/Tiff/flower-miniswhite-16.tiff b/tests/Images/Input/Tiff/flower-miniswhite-16_msb.tiff
similarity index 100%
rename from tests/Images/Input/Tiff/flower-miniswhite-16.tiff
rename to tests/Images/Input/Tiff/flower-miniswhite-16_msb.tiff
diff --git a/tests/Images/Input/Tiff/flower-miniswhite-32.tiff b/tests/Images/Input/Tiff/flower-miniswhite-32.tiff
deleted file mode 100644
index f8cf87553e..0000000000
--- a/tests/Images/Input/Tiff/flower-miniswhite-32.tiff
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:514417ead3d6c5c6ca33374ef0bb6ecbe5f875a266519d4cbaa4a6b91033d243
-size 12778
diff --git a/tests/Images/Input/Tiff/flower-miniswhite-32_lsb.tiff b/tests/Images/Input/Tiff/flower-miniswhite-32_lsb.tiff
index 8c99dda7fb..d44ae9b915 100644
--- a/tests/Images/Input/Tiff/flower-miniswhite-32_lsb.tiff
+++ b/tests/Images/Input/Tiff/flower-miniswhite-32_lsb.tiff
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:64c948aa03bc4a24cd1d68bb18b5031c119936154a90f1cb1d9aaabd854c5d9b
+oid sha256:17139bc00d9fe2905fbac9226120d2823ea17a10a39b140970122153ec23265d
size 12778
diff --git a/tests/Images/Input/Tiff/flower-miniswhite-32_msb.tiff b/tests/Images/Input/Tiff/flower-miniswhite-32_msb.tiff
new file mode 100644
index 0000000000..d44ae9b915
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-miniswhite-32_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:17139bc00d9fe2905fbac9226120d2823ea17a10a39b140970122153ec23265d
+size 12778
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 0000000000..3241c8fbec
--- /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 0000000000..5623a74286
--- /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
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb_zip_predictor.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb_zip_predictor.tiff
new file mode 100644
index 0000000000..69fe133f8c
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-16_lsb_zip_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb465c21009def345d192319ba13ba2e1e537310eec3ab2cce680f0d111a4f18
+size 18256
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-16_msb_zip_predictor.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-16_msb_zip_predictor.tiff
new file mode 100644
index 0000000000..231de2eaad
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-16_msb_zip_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c098beb9c8f250e9d4f6eb66a3a42f3852ad3ca86aabbdbacfa897d93ec8bc0d
+size 18254
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-32_lsb_zip_predictor.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-32_lsb_zip_predictor.tiff
new file mode 100644
index 0000000000..ca3fee9da5
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-32_lsb_zip_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3ed60ad91ea70db01789f9dd37745d8a5ab86f72b98637cf2007b4e28a71976f
+size 37632
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-32_msb_zip_predictor.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-32_msb_zip_predictor.tiff
new file mode 100644
index 0000000000..07a1ae74f7
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-32_msb_zip_predictor.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1086c2ec568c5c3c2b08fcc66691fab017eb6fad109373a1dadd2e12fae86bc8
+size 37630
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 0000000000..da0f104387
--- /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 0000000000..353771db94
--- /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