Browse Source

Undo horizontal prediction for each tile row in case of tiled tiff's

pull/2878/head
Brian Popow 1 year ago
parent
commit
f72be91d10
  1. 9
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
  2. 91
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  3. 5
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  4. 15
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  5. 2
      tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs

9
src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs

@ -22,6 +22,8 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor
private readonly TiffColorType colorType;
private readonly bool isTiled;
/// <summary>
/// Initializes a new instance of the <see cref="DeflateTiffCompression" /> class.
/// </summary>
@ -31,11 +33,13 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor
/// <param name="colorType">The color type of the pixel data.</param>
/// <param name="predictor">The tiff predictor used.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian)
/// <param name="isTiled">Flag indicates, if the image is a tiled image.</param>
public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian, bool isTiled)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
this.colorType = colorType;
this.isBigEndian = isBigEndian;
this.isTiled = isTiled;
}
/// <inheritdoc/>
@ -68,7 +72,8 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor
}
}
if (this.Predictor == TiffPredictor.Horizontal)
// When the image is tiled, undoing the horizontal predictor will be done for each tile row in the DecodeTilesChunky() method.
if (this.Predictor == TiffPredictor.Horizontal && !this.isTiled)
{
HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian);
}

91
src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs

@ -62,6 +62,22 @@ internal static class HorizontalPredictor
}
}
public static void UndoRow(Span<byte> pixelBytes, int width, int y, TiffColorType colorType)
{
// TODO: Implement missing colortypes, see above.
switch (colorType)
{
case TiffColorType.Rgb888:
case TiffColorType.CieLab:
UndoRgb24BitRow(pixelBytes, width, y);
break;
case TiffColorType.Rgba8888:
case TiffColorType.Cmyk:
UndoRgba32BitRow(pixelBytes, width, y);
break;
}
}
public static void ApplyHorizontalPrediction(Span<byte> rows, int width, int bitsPerPixel)
{
if (bitsPerPixel == 8)
@ -257,27 +273,56 @@ internal static class HorizontalPredictor
}
}
private static void UndoRgb24BitRow(Span<byte> pixelBytes, int width, int y)
{
int rowBytesCount = width * 3;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
Span<Rgb24> rowRgb = MemoryMarshal.Cast<byte, Rgb24>(rowBytes)[..width];
ref Rgb24 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
byte r = rowRgbBase.R;
byte g = rowRgbBase.G;
byte b = rowRgbBase.B;
for (int x = 1; x < rowRgb.Length; x++)
{
ref Rgb24 pixel = ref rowRgb[x];
r += pixel.R;
g += pixel.G;
b += pixel.B;
pixel = new Rgb24(r, g, b);
}
}
private static void UndoRgb24Bit(Span<byte> pixelBytes, int width)
{
int rowBytesCount = width * 3;
int height = pixelBytes.Length / rowBytesCount;
for (int y = 0; y < height; y++)
{
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
Span<Rgb24> rowRgb = MemoryMarshal.Cast<byte, Rgb24>(rowBytes)[..width];
ref Rgb24 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
byte r = rowRgbBase.R;
byte g = rowRgbBase.G;
byte b = rowRgbBase.B;
UndoRgb24BitRow(pixelBytes, width, y);
}
}
for (int x = 1; x < rowRgb.Length; x++)
{
ref Rgb24 pixel = ref rowRgb[x];
r += pixel.R;
g += pixel.G;
b += pixel.B;
pixel = new Rgb24(r, g, b);
}
private static void UndoRgba32BitRow(Span<byte> pixelBytes, int width, int y)
{
int rowBytesCount = width * 4;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
Span<Rgba32> rowRgb = MemoryMarshal.Cast<byte, Rgba32>(rowBytes)[..width];
ref Rgba32 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
byte r = rowRgbBase.R;
byte g = rowRgbBase.G;
byte b = rowRgbBase.B;
byte a = rowRgbBase.A;
for (int x = 1; x < rowRgb.Length; x++)
{
ref Rgba32 pixel = ref rowRgb[x];
r += pixel.R;
g += pixel.G;
b += pixel.B;
a += pixel.A;
pixel = new Rgba32(r, g, b, a);
}
}
@ -287,23 +332,7 @@ internal static class HorizontalPredictor
int height = pixelBytes.Length / rowBytesCount;
for (int y = 0; y < height; y++)
{
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
Span<Rgba32> rowRgb = MemoryMarshal.Cast<byte, Rgba32>(rowBytes)[..width];
ref Rgba32 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
byte r = rowRgbBase.R;
byte g = rowRgbBase.G;
byte b = rowRgbBase.B;
byte a = rowRgbBase.A;
for (int x = 1; x < rowRgb.Length; x++)
{
ref Rgba32 pixel = ref rowRgb[x];
r += pixel.R;
g += pixel.G;
b += pixel.B;
a += pixel.A;
pixel = new Rgba32(r, g, b, a);
}
UndoRgba32BitRow(pixelBytes, width, y);
}
}

5
src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs

@ -24,7 +24,8 @@ internal static class TiffDecompressorsFactory
byte[] jpegTables,
uint oldJpegStartOfImageMarker,
TiffFillOrder fillOrder,
ByteOrder byteOrder)
ByteOrder byteOrder,
bool isTiled = false)
{
switch (method)
{
@ -40,7 +41,7 @@ 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);
return new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian, isTiled);
case TiffDecoderCompressionType.Lzw:
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");

15
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -683,7 +683,8 @@ internal class TiffDecoderCore : ImageDecoderCore
Span<byte> tileBufferSpan = tileBuffer.GetSpan();
Span<byte> uncompressedPixelBufferSpan = uncompressedPixelBuffer.GetSpan();
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel);
bool isTiled = true;
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel, isTiled);
TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>();
int tileIndex = 0;
@ -712,6 +713,13 @@ internal class TiffDecoderCore : ImageDecoderCore
{
Span<byte> uncompressedPixelRow = uncompressedPixelBufferSpan.Slice(uncompressedPixelBufferOffset, bytesToCopy);
tileBufferSpan.Slice(tileBufferOffset, bytesToCopy).CopyTo(uncompressedPixelRow);
// Undo the horziontal predictor for each tile row.
if (this.Predictor == TiffPredictor.Horizontal)
{
HorizontalPredictor.UndoRow(uncompressedPixelRow, tileLength, 0, this.ColorType);
}
tileBufferOffset += bytesPerTileRow;
uncompressedPixelBufferOffset += bytesPerRow;
}
@ -750,7 +758,7 @@ internal class TiffDecoderCore : ImageDecoderCore
this.YcbcrSubSampling,
this.byteOrder);
private TiffBaseDecompressor CreateDecompressor<TPixel>(int frameWidth, int bitsPerPixel)
private TiffBaseDecompressor CreateDecompressor<TPixel>(int frameWidth, int bitsPerPixel, bool isTiled = false)
where TPixel : unmanaged, IPixel<TPixel> =>
TiffDecompressorsFactory.Create(
this.Options,
@ -765,7 +773,8 @@ internal class TiffDecoderCore : ImageDecoderCore
this.JpegTables,
this.OldJpegCompressionStartOfImageMarker.GetValueOrDefault(),
this.FillOrder,
this.byteOrder);
this.byteOrder,
isTiled);
private IMemoryOwner<ulong> ConvertNumbers(Array array, out Span<ulong> span)
{

2
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);
using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false);
decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default);

Loading…
Cancel
Save