From 18f8de983ebb837f9b98d25ce5cef72839b3741b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 22 Feb 2025 19:40:13 +0100 Subject: [PATCH] Do not ignore tileHeight when undoing horizontal predictor --- .../Compression/Decompressors/DeflateTiffCompression.cs | 8 ++++++-- .../Tiff/Compression/Decompressors/LzwTiffCompression.cs | 8 ++++++-- .../Formats/Tiff/Compression/HorizontalPredictor.cs | 7 ++++--- .../Formats/Tiff/Compression/TiffDecompressorsFactory.cs | 7 ++++--- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 7 ++++--- .../Tiff/Compression/DeflateTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/LzwTiffCompressionTests.cs | 2 +- 7 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index 3fcbd292d..64e702f1b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -26,6 +26,8 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor private readonly int tileWidth; + private readonly int tileHeight; + /// /// Initializes a new instance of the class. /// @@ -37,13 +39,15 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor /// if set to true decodes the pixel data as big endian, otherwise as little endian. /// Flag indicates, if the image is a tiled image. /// Number of pixels in a tile row. - public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian, bool isTiled, int tileWidth) + /// Number of rows in a tile. + public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian, bool isTiled, int tileWidth, int tileHeight) : base(memoryAllocator, width, bitsPerPixel, predictor) { this.colorType = colorType; this.isBigEndian = isBigEndian; this.isTiled = isTiled; this.tileWidth = tileWidth; + this.tileHeight = tileHeight; } /// @@ -81,7 +85,7 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor if (this.isTiled) { // When the image is tiled, undoing the horizontal predictor will be done for each tile row. - HorizontalPredictor.UndoTile(buffer, this.tileWidth, this.colorType, this.isBigEndian); + HorizontalPredictor.UndoTile(buffer, this.tileWidth, this.tileHeight, this.colorType, this.isBigEndian); } else { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index 5995cbc8f..240292718 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -21,6 +21,8 @@ internal sealed class LzwTiffCompression : TiffBaseDecompressor private readonly int tileWidth; + private readonly int tileHeight; + /// /// Initializes a new instance of the class. /// @@ -32,13 +34,15 @@ internal sealed class LzwTiffCompression : TiffBaseDecompressor /// if set to true decodes the pixel data as big endian, otherwise as little endian. /// Flag indicates, if the image is a tiled image. /// Number of pixels in a tile row. - public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian, bool isTiled, int tileWidth) + /// Number of rows in a tile. + public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian, bool isTiled, int tileWidth, int tileHeight) : base(memoryAllocator, width, bitsPerPixel, predictor) { this.colorType = colorType; this.isBigEndian = isBigEndian; this.isTiled = isTiled; this.tileWidth = tileWidth; + this.tileHeight = tileHeight; } /// @@ -52,7 +56,7 @@ internal sealed class LzwTiffCompression : TiffBaseDecompressor if (this.isTiled) { // When the image is tiled, undoing the horizontal predictor will be done for each tile row. - HorizontalPredictor.UndoTile(buffer, this.tileWidth, this.colorType, this.isBigEndian); + HorizontalPredictor.UndoTile(buffer, this.tileWidth, this.tileHeight, this.colorType, this.isBigEndian); } else { diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index 6bd0230a1..706e6a38c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -66,12 +66,13 @@ internal static class HorizontalPredictor /// Inverts the horizontal predictor for each tile row. /// /// Buffer with decompressed pixel data for a tile. - /// Tile with in pixels. + /// Tile width in pixels. + /// Tile height in pixels. /// 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 UndoTile(Span pixelBytes, int tileWidth, TiffColorType colorType, bool isBigEndian) + public static void UndoTile(Span pixelBytes, int tileWidth, int tileHeight, TiffColorType colorType, bool isBigEndian) { - for (int y = 0; y < tileWidth; y++) + for (int y = 0; y < tileHeight; y++) { UndoRow(pixelBytes, tileWidth, y, colorType, isBigEndian); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index eec1efcb2..3e1df261b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -25,7 +25,8 @@ internal static class TiffDecompressorsFactory TiffFillOrder fillOrder, ByteOrder byteOrder, bool isTiled = false, - int tileWidth = 0) + int tileWidth = 0, + int tileHeight = 0) { switch (method) { @@ -41,11 +42,11 @@ internal static class TiffDecompressorsFactory case TiffDecoderCompressionType.Deflate: DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); - return new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian, isTiled, tileWidth); + return new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian, isTiled, tileWidth, tileHeight); case TiffDecoderCompressionType.Lzw: DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected"); - return new LzwTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian, isTiled, tileWidth); + return new LzwTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian, isTiled, tileWidth, tileHeight); 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 c642f6734..d8ebb1e9e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -679,7 +679,7 @@ internal class TiffDecoderCore : ImageDecoderCore using IMemoryOwner tileBuffer = this.memoryAllocator.Allocate(bytesPerTileRow * tileLength, AllocationOptions.Clean); Span tileBufferSpan = tileBuffer.GetSpan(); - using TiffBaseDecompressor decompressor = this.CreateDecompressor(frame.Width, bitsPerPixel, true, tileWidth); + using TiffBaseDecompressor decompressor = this.CreateDecompressor(frame.Width, bitsPerPixel, true, tileWidth, tileLength); TiffBaseColorDecoder colorDecoder = this.CreateChunkyColorDecoder(); int tileIndex = 0; @@ -747,7 +747,7 @@ internal class TiffDecoderCore : ImageDecoderCore this.YcbcrSubSampling, this.byteOrder); - private TiffBaseDecompressor CreateDecompressor(int frameWidth, int bitsPerPixel, bool isTiled = false, int tileWidth = 0) + private TiffBaseDecompressor CreateDecompressor(int frameWidth, int bitsPerPixel, bool isTiled = false, int tileWidth = 0, int tileHeight = 0) where TPixel : unmanaged, IPixel => TiffDecompressorsFactory.Create( this.Options, @@ -764,7 +764,8 @@ internal class TiffDecoderCore : ImageDecoderCore this.FillOrder, this.byteOrder, isTiled, - tileWidth); + tileWidth, + tileHeight); private IMemoryOwner ConvertNumbers(Array array, out Span span) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 3db142d6d..b16119f33 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -23,7 +23,7 @@ public class DeflateTiffCompressionTests using BufferedReadStream stream = CreateCompressedStream(data); byte[] buffer = new byte[data.Length]; - using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0); + using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0, 0); decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 86da73186..8c21e346a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -37,7 +37,7 @@ public class LzwTiffCompressionTests using BufferedReadStream stream = CreateCompressedStream(data); byte[] buffer = new byte[data.Length]; - using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0); + using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0, 0); decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); Assert.Equal(data, buffer);