diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
index 2188913bc3..ef99832f70 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
@@ -5,7 +5,6 @@ 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.IO;
using SixLabors.ImageSharp.Memory;
@@ -20,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
///
internal class DeflateTiffCompression : TiffBaseDecompressor
{
+ private readonly bool isBigEndian;
+
///
/// Initializes a new instance of the class.
///
@@ -27,10 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// The image width.
/// The bits used per pixel.
/// The tiff predictor used.
- public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor)
- : base(memoryAllocator, width, bitsPerPixel, predictor)
- {
- }
+ /// 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;
///
protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer)
@@ -62,7 +62,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.BitsPerPixel, this.isBigEndian);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
index 7e75dd4f05..0fe1b60ecf 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
@@ -13,6 +13,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
///
internal class LzwTiffCompression : TiffBaseDecompressor
{
+ private readonly bool isBigEndian;
+
///
/// Initializes a new instance of the class.
///
@@ -20,10 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// The image width.
/// The bits used per pixel.
/// The tiff predictor used.
- public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor)
- : base(memoryAllocator, width, bitsPerPixel, predictor)
- {
- }
+ /// 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;
///
protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer)
@@ -33,7 +34,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.BitsPerPixel, this.isBigEndian);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
index ae2f17dbb5..da84b51b39 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
@@ -2,9 +2,10 @@
// 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.Utils;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
@@ -20,12 +21,17 @@ 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)
+ /// 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)
{
if (bitsPerPixel == 8)
{
Undo8Bit(pixelBytes, width);
}
+ else if (bitsPerPixel == 16)
+ {
+ Undo16Bit(pixelBytes, width, isBigEndian);
+ }
else if (bitsPerPixel == 24)
{
Undo24Bit(pixelBytes, width);
@@ -110,6 +116,50 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
+ private static void Undo16Bit(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 Undo24Bit(Span pixelBytes, int width)
{
int rowBytesCount = width * 3;
diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
index a6d44f4d3c..964f0bf127 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
@@ -16,7 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
int width,
int bitsPerPixel,
TiffPredictor predictor,
- FaxCompressionOptions faxOptions)
+ FaxCompressionOptions faxOptions,
+ ByteOrder byteOrder)
{
switch (method)
{
@@ -32,11 +33,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, 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, 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/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 63185619d4..2c7e8d0c8f 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -264,7 +264,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
stripBuffers[stripIndex] = this.memoryAllocator.Allocate(uncompressedStripSize);
}
- using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
+ using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(
+ this.CompressionType,
+ this.memoryAllocator,
+ this.PhotometricInterpretation,
+ frame.Width,
+ bitsPerPixel,
+ this.Predictor,
+ this.FaxCompressionOptions,
+ this.byteOrder);
TiffBasePlanarColorDecoder colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder);
@@ -314,7 +322,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
frame.Width,
bitsPerPixel,
this.Predictor,
- this.FaxCompressionOptions);
+ this.FaxCompressionOptions,
+ this.byteOrder);
TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.Configuration, this.ColorType, this.BitsPerSample, this.ColorMap, this.byteOrder);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
index 782a504d58..e9399e23b5 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
@@ -26,7 +26,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, 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..834d206cc7 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
@@ -38,7 +38,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, 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 182fafa33e..bba27039dd 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)]
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 8eaaec4935..08b0b2901f 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -599,6 +599,8 @@ namespace SixLabors.ImageSharp.Tests
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 Flower16BitGrayPredictorBigEndian = "Tiff/flower-minisblack-16_msb_lzw_predictor.tiff";
+ public const string Flower16BitGrayPredictorLittleEndian = "Tiff/flower-minisblack-16_lsb_lzw_predictor.tiff";
public const string Flower24BitGray = "Tiff/flower-minisblack-24.tiff";
public const string Flower24BitGrayLittleEndian = "Tiff/flower-minisblack-24_lsb.tiff";
public const string Flower32BitGray = "Tiff/flower-minisblack-32.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