From f11fbc14d915394d5c09d581107e276904b1e6cc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 8 Feb 2025 18:38:24 +0100 Subject: [PATCH] Add test cases for tiled 16 bit gray tiff's --- .../Tiff/Compression/HorizontalPredictor.cs | 80 ++++++++++++------- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Formats/Tiff/TiffDecoderTests.cs | 2 + tests/ImageSharp.Tests/TestImages.cs | 2 + ...g_endian_deflate_compressed_predictor.tiff | 3 + ...e_endian_deflate_compressed_predictor.tiff | 3 + 6 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff create mode 100644 tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index b60f3cb0d4..e37a83528e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -62,7 +62,7 @@ internal static class HorizontalPredictor } } - public static void UndoRow(Span pixelBytes, int width, int y, TiffColorType colorType) + public static void UndoRow(Span pixelBytes, int width, int y, TiffColorType colorType, bool isBigEndian) { // TODO: Implement missing colortypes, see above. switch (colorType) @@ -71,6 +71,18 @@ internal static class HorizontalPredictor case TiffColorType.WhiteIsZero8: case TiffColorType.PaletteColor: UndoGray8BitRow(pixelBytes, width, y); + break; + case TiffColorType.BlackIsZero16: + case TiffColorType.WhiteIsZero16: + if (isBigEndian) + { + UndoGray16BitBigEndianRow(pixelBytes, width, y); + } + else + { + UndoGray16BitLittleEndianRow(pixelBytes, width, y); + } + break; case TiffColorType.Rgb888: case TiffColorType.CieLab: @@ -196,6 +208,44 @@ internal static class HorizontalPredictor } } + private static void UndoGray16BitBigEndianRow(Span pixelBytes, int width, int y) + { + int rowBytesCount = width * 2; + int height = pixelBytes.Length / rowBytesCount; + int offset = 0; + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + ushort pixelValue = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); + offset += 2; + + for (int x = 1; x < width; x++) + { + Span rowSpan = rowBytes.Slice(offset, 2); + ushort diff = TiffUtilities.ConvertToUShortBigEndian(rowSpan); + pixelValue += diff; + BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue); + offset += 2; + } + } + + private static void UndoGray16BitLittleEndianRow(Span pixelBytes, int width, int y) + { + int rowBytesCount = width * 2; + int height = pixelBytes.Length / rowBytesCount; + int offset = 0; + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + ushort pixelValue = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); + offset += 2; + + for (int x = 1; x < width; x++) + { + Span rowSpan = rowBytes.Slice(offset, 2); + ushort diff = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); + pixelValue += diff; + BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue); + offset += 2; + } + } + private static void UndoGray16Bit(Span pixelBytes, int width, bool isBigEndian) { int rowBytesCount = width * 2; @@ -204,38 +254,14 @@ internal static class HorizontalPredictor { for (int y = 0; y < height; y++) { - int offset = 0; - Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort pixelValue = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); - offset += 2; - - for (int x = 1; x < width; x++) - { - Span rowSpan = rowBytes.Slice(offset, 2); - ushort diff = TiffUtilities.ConvertToUShortBigEndian(rowSpan); - pixelValue += diff; - BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue); - offset += 2; - } + UndoGray16BitBigEndianRow(pixelBytes, width, y); } } else { for (int y = 0; y < height; y++) { - int offset = 0; - Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - ushort pixelValue = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); - offset += 2; - - for (int x = 1; x < width; x++) - { - Span rowSpan = rowBytes.Slice(offset, 2); - ushort diff = TiffUtilities.ConvertToUShortLittleEndian(rowSpan); - pixelValue += diff; - BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue); - offset += 2; - } + UndoGray16BitLittleEndianRow(pixelBytes, width, y); } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 1c15646970..1b23a6e2b7 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -718,7 +718,7 @@ internal class TiffDecoderCore : ImageDecoderCore if (this.Predictor == TiffPredictor.Horizontal) { int pixelsInTileRow = isLastHorizontalTile ? remainingPixelsInRow : tileLength; - HorizontalPredictor.UndoRow(uncompressedPixelRow, pixelsInTileRow, 0, this.ColorType); + HorizontalPredictor.UndoRow(uncompressedPixelRow, pixelsInTileRow, 0, this.ColorType, this.byteOrder == ByteOrder.BigEndian); } tileBufferOffset += bytesPerTileRow; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index e3cb4bf9b8..00f498e9ae 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -90,6 +90,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(TiledRgbaDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledRgbDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledGrayDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Tiled(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8bc9d131fe..fee925af3c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -989,6 +989,8 @@ public static class TestImages public const string TiledRgbaDeflateCompressedWithPredictor = "Tiff/tiled_rgba_deflate_compressed_predictor.tiff"; public const string TiledRgbDeflateCompressedWithPredictor = "Tiff/tiled_rgb_deflate_compressed_predictor.tiff"; public const string TiledGrayDeflateCompressedWithPredictor = "Tiff/tiled_gray_deflate_compressed_predictor.tiff"; + public const string TiledGray16BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledGray16BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff"; // Images with alpha channel. public const string Rgba2BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha2bit.tiff"; diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..778601cd8b --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e267cf78bebba51628f0aca98c8720e8ea8013b9e5f96c8f12df1c153f34f065 +size 80195 diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..2fbe32e6fa --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19b4a2ee8761e0016a598f66f12eb62edf4c4d30f33694d30986ce84516ac454 +size 80177