From dec1dc1c6b36c364c44967b12919bbe320ac2bc3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 11 Feb 2025 19:17:27 +0100 Subject: [PATCH] Add test cases for tiled tiff, deflate compressed with predictor and color type Rgba with 32 bit for each channel --- .../Tiff/Compression/HorizontalPredictor.cs | 185 +++++++++++------- .../Formats/Tiff/TiffDecoderTests.cs | 6 +- tests/ImageSharp.Tests/TestImages.cs | 2 + ...g_endian_deflate_compressed_predictor.tiff | 3 + ...e_endian_deflate_compressed_predictor.tiff | 3 + 5 files changed, 122 insertions(+), 77 deletions(-) create mode 100644 tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff create mode 100644 tests/Images/Input/Tiff/tiled_rgba_128bit_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 a2b28e6f7..c7f25f118 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression; internal static class HorizontalPredictor { /// - /// Inverts the horizontal prediction. + /// Inverts the horizontal predictor. /// /// Buffer with decompressed pixel data. /// The width of the image or strip. @@ -62,9 +62,16 @@ internal static class HorizontalPredictor } } + /// + /// Inverts the horizontal predictor for one row. + /// + /// Buffer with decompressed pixel data. + /// The width in pixels of the row. + /// The row index. + /// 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 UndoRow(Span pixelBytes, int width, int y, TiffColorType colorType, bool isBigEndian) { - // TODO: Implement missing colortypes, see above. switch (colorType) { case TiffColorType.BlackIsZero8: @@ -143,6 +150,18 @@ internal static class HorizontalPredictor UndoRgb96BitLittleEndianRow(pixelBytes, width, y); } + break; + + case TiffColorType.Rgba32323232: + if (isBigEndian) + { + UndoRgba128BitBigEndianRow(pixelBytes, width, y); + } + else + { + UndoRgba128BitLittleEndianRow(pixelBytes, width, y); + } + break; } } @@ -729,6 +748,92 @@ internal static class HorizontalPredictor } } + private static void UndoRgba128BitBigEndianRow(Span pixelBytes, int width, int y) + { + int rowBytesCount = width * 16; + + int offset = 0; + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint a = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); + offset += 4; + + for (int x = 1; x < width; x++) + { + Span rowSpan = rowBytes.Slice(offset, 4); + uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan); + r += deltaR; + BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan); + g += deltaG; + BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan); + b += deltaB; + BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaA = TiffUtilities.ConvertToUIntBigEndian(rowSpan); + a += deltaA; + BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a); + offset += 4; + } + } + + private static void UndoRgba128BitLittleEndianRow(Span pixelBytes, int width, int y) + { + int rowBytesCount = width * 16; + + int offset = 0; + Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); + uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + offset += 4; + uint a = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); + offset += 4; + + for (int x = 1; x < width; x++) + { + Span rowSpan = rowBytes.Slice(offset, 4); + uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); + r += deltaR; + BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); + g += deltaG; + BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); + b += deltaB; + BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); + offset += 4; + + rowSpan = rowBytes.Slice(offset, 4); + uint deltaA = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); + a += deltaA; + BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a); + offset += 4; + } + } + private static void UndoRgba128Bit(Span pixelBytes, int width, bool isBigEndian) { int rowBytesCount = width * 16; @@ -737,86 +842,14 @@ internal static class HorizontalPredictor { for (int y = 0; y < height; y++) { - int offset = 0; - Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint a = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); - offset += 4; - - for (int x = 1; x < width; x++) - { - Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan); - r += deltaR; - BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan); - g += deltaG; - BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan); - b += deltaB; - BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaA = TiffUtilities.ConvertToUIntBigEndian(rowSpan); - a += deltaA; - BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a); - offset += 4; - } + UndoRgba128BitBigEndianRow(pixelBytes, width, y); } } else { for (int y = 0; y < height; y++) { - int offset = 0; - Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); - uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); - offset += 4; - uint a = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); - offset += 4; - - for (int x = 1; x < width; x++) - { - Span rowSpan = rowBytes.Slice(offset, 4); - uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); - r += deltaR; - BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); - g += deltaG; - BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); - b += deltaB; - BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); - offset += 4; - - rowSpan = rowBytes.Slice(offset, 4); - uint deltaA = TiffUtilities.ConvertToUIntLittleEndian(rowSpan); - a += deltaA; - BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a); - offset += 4; - } + UndoRgba128BitLittleEndianRow(pixelBytes, width, y); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 2ca081eff..16d3ade0e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -87,6 +87,10 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(QuadTile, PixelTypes.Rgba32)] [WithFile(TiledChunky, PixelTypes.Rgba32)] [WithFile(TiledPlanar, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Tiled(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] [WithFile(TiledRgbaDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledRgbDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledGrayDeflateCompressedWithPredictor, PixelTypes.Rgba32)] @@ -100,7 +104,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(TiledRgba64BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledRgb96BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] [WithFile(TiledRgb96BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] - public void TiffDecoder_CanDecode_Tiled(TestImageProvider provider) + public void TiffDecoder_CanDecode_Tiled_Deflate_Compressed(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); [Theory] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 51a56e152..c65f9ba5e 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -999,6 +999,8 @@ public static class TestImages public const string TiledRgba64BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_64bit_big_endian_deflate_compressed_predictor.tiff"; public const string TiledRgb96BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_96bit_little_endian_deflate_compressed_predictor.tiff"; public const string TiledRgb96BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_96bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba128BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba128BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_128bit_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_rgba_128bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 000000000..27055256e --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d3f46e8555f014ef3a7b848c7e2e88645913c01fc70b6aeb215e9a2385a635f +size 239355 diff --git a/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 000000000..b29d10c82 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39029aaf35c923c2136e35b9fd5fae1e9c9178791e47714b8f26cb53a0091608 +size 239441