diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
index ef99832f70..bb57853d53 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
@@ -6,6 +6,7 @@ using System.IO.Compression;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
+using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -21,16 +22,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
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.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor, bool isBigEndian)
- : base(memoryAllocator, width, bitsPerPixel, predictor) => this.isBigEndian = isBigEndian;
+ 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;
+ }
///
protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer)
@@ -62,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
- HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel, this.isBigEndian);
+ 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 0fe1b60ecf..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;
@@ -15,16 +16,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
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.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor, bool isBigEndian)
- : base(memoryAllocator, width, bitsPerPixel, predictor) => this.isBigEndian = isBigEndian;
+ 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;
+ }
///
protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer)
@@ -34,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
- HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel, this.isBigEndian);
+ 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 da84b51b39..3685167d53 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
@@ -5,6 +5,7 @@ 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;
@@ -20,21 +21,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
///
/// Buffer with decompressed pixel data.
/// The width of the image or strip.
- /// Bits per pixel.
+ /// 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, int bitsPerPixel, bool isBigEndian)
+ public static void Undo(Span pixelBytes, int width, TiffColorType colorType, bool isBigEndian)
{
- if (bitsPerPixel == 8)
- {
- Undo8Bit(pixelBytes, width);
- }
- else if (bitsPerPixel == 16)
+ switch (colorType)
{
- Undo16Bit(pixelBytes, width, isBigEndian);
- }
- else if (bitsPerPixel == 24)
- {
- 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;
}
}
@@ -99,7 +107,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;
@@ -116,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
- private static void Undo16Bit(Span pixelBytes, int width, bool isBigEndian)
+ private static void UndoGray16Bit(Span pixelBytes, int width, bool isBigEndian)
{
int rowBytesCount = width * 2;
int height = pixelBytes.Length / rowBytesCount;
@@ -160,7 +168,51 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
- private static void Undo24Bit(Span pixelBytes, int width)
+ 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;
diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
index 964f0bf127..083e53950a 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,6 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
TiffPhotometricInterpretation photometricInterpretation,
int width,
int bitsPerPixel,
+ TiffColorType colorType,
TiffPredictor predictor,
FaxCompressionOptions faxOptions,
ByteOrder byteOrder)
@@ -33,11 +35,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, byteOrder == ByteOrder.BigEndian);
+ 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, byteOrder == ByteOrder.BigEndian);
+ 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/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/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 2c7e8d0c8f..37bc01ad18 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -270,6 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
+ this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
this.byteOrder);
@@ -321,6 +322,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
+ this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
this.byteOrder);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
index e9399e23b5..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, false);
+ 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 834d206cc7..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, false);
+ 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 bba27039dd..a8460e6c93 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -197,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)
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 08b0b2901f..ff6c198c4d 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 Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.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-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_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