Browse Source

Add support for horizontal predictor for 32 bit gray tiff's

pull/1729/head
Brian Popow 5 years ago
parent
commit
78efd684d7
  1. 14
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
  2. 14
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
  3. 82
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  4. 6
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  5. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
  6. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  7. 3
      tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
  8. 3
      tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
  9. 11
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  10. 2
      tests/ImageSharp.Tests/TestImages.cs
  11. 3
      tests/Images/Input/Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff
  12. 3
      tests/Images/Input/Tiff/flower-minisblack-32_msb_deflate_predictor.tiff

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

@ -6,6 +6,7 @@ using System.IO.Compression;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -21,16 +22,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
private readonly bool isBigEndian;
private readonly TiffColorType colorType;
/// <summary>
/// Initializes a new instance of the <see cref="DeflateTiffCompression" /> class.
/// </summary>
/// <param name="memoryAllocator">The memoryAllocator to use for buffer allocations.</param>
/// <param name="width">The image width.</param>
/// <param name="bitsPerPixel">The bits used per pixel.</param>
/// <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, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor) => this.isBigEndian = isBigEndian;
public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
this.colorType = colorType;
this.isBigEndian = isBigEndian;
}
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, Span<byte> buffer)
@ -62,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel, this.isBigEndian);
HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian);
}
}

14
src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs

@ -3,6 +3,7 @@
using System;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -15,16 +16,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
private readonly bool isBigEndian;
private readonly TiffColorType colorType;
/// <summary>
/// Initializes a new instance of the <see cref="LzwTiffCompression" /> class.
/// </summary>
/// <param name="memoryAllocator">The memoryAllocator to use for buffer allocations.</param>
/// <param name="width">The image width.</param>
/// <param name="bitsPerPixel">The bits used per pixel.</param>
/// <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 LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor) => this.isBigEndian = isBigEndian;
public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
this.colorType = colorType;
this.isBigEndian = isBigEndian;
}
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, Span<byte> buffer)
@ -34,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
if (this.Predictor == TiffPredictor.Horizontal)
{
HorizontalPredictor.Undo(buffer, this.Width, this.BitsPerPixel, this.isBigEndian);
HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian);
}
}

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

@ -5,6 +5,7 @@ using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.PixelFormats;
@ -20,21 +21,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
/// </summary>
/// <param name="pixelBytes">Buffer with decompressed pixel data.</param>
/// <param name="width">The width of the image or strip.</param>
/// <param name="bitsPerPixel">Bits per pixel.</param>
/// <param name="colorType">The color type of the pixel data.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public static void Undo(Span<byte> pixelBytes, int width, int bitsPerPixel, bool isBigEndian)
public static void Undo(Span<byte> pixelBytes, int width, TiffColorType colorType, bool isBigEndian)
{
if (bitsPerPixel == 8)
{
Undo8Bit(pixelBytes, width);
}
else if (bitsPerPixel == 16)
switch (colorType)
{
Undo16Bit(pixelBytes, width, isBigEndian);
}
else if (bitsPerPixel == 24)
{
Undo24Bit(pixelBytes, width);
case TiffColorType.BlackIsZero8:
case TiffColorType.WhiteIsZero8:
case TiffColorType.PaletteColor:
UndoGray8Bit(pixelBytes, width);
break;
case TiffColorType.BlackIsZero16:
case TiffColorType.WhiteIsZero16:
UndoGray16Bit(pixelBytes, width, isBigEndian);
break;
case TiffColorType.BlackIsZero32:
case TiffColorType.WhiteIsZero32:
UndoGray32Bit(pixelBytes, width, isBigEndian);
break;
case TiffColorType.Rgb888:
UndoRgb24Bit(pixelBytes, width);
break;
}
}
@ -99,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
private static void Undo8Bit(Span<byte> pixelBytes, int width)
private static void UndoGray8Bit(Span<byte> pixelBytes, int width)
{
int rowBytesCount = width;
int height = pixelBytes.Length / rowBytesCount;
@ -116,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
private static void Undo16Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
private static void UndoGray16Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
{
int rowBytesCount = width * 2;
int height = pixelBytes.Length / rowBytesCount;
@ -160,7 +168,51 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
private static void Undo24Bit(Span<byte> pixelBytes, int width)
private static void UndoGray32Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
{
int rowBytesCount = width * 4;
int height = pixelBytes.Length / rowBytesCount;
if (isBigEndian)
{
for (int y = 0; y < height; y++)
{
int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint pixelValue = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4;
for (int x = 1; x < width; x++)
{
Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint diff = TiffUtils.ConvertToUIntBigEndian(rowSpan);
pixelValue += diff;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, pixelValue);
offset += 4;
}
}
}
else
{
for (int y = 0; y < height; y++)
{
int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint pixelValue = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4;
for (int x = 1; x < width; x++)
{
Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint diff = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
pixelValue += diff;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, pixelValue);
offset += 4;
}
}
}
}
private static void UndoRgb24Bit(Span<byte> pixelBytes, int width)
{
int rowBytesCount = width * 3;
int height = pixelBytes.Length / rowBytesCount;

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

@ -3,6 +3,7 @@
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
@ -15,6 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
TiffPhotometricInterpretation photometricInterpretation,
int width,
int bitsPerPixel,
TiffColorType colorType,
TiffPredictor predictor,
FaxCompressionOptions faxOptions,
ByteOrder byteOrder)
@ -33,11 +35,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, byteOrder == ByteOrder.BigEndian);
return new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, 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, byteOrder == ByteOrder.BigEndian);
return new LzwTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian);
case TiffDecoderCompressionType.T4:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

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

@ -270,6 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
this.byteOrder);
@ -321,6 +322,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation,
frame.Width,
bitsPerPixel,
this.ColorType,
this.Predictor,
this.FaxCompressionOptions,
this.byteOrder);

3
tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs

@ -5,6 +5,7 @@ using System.IO;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using Xunit;
@ -26,7 +27,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, false);
using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false);
decompressor.Decompress(stream, 0, (uint)stream.Length, buffer);

3
tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs

@ -5,6 +5,7 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.IO;
using Xunit;
@ -38,7 +39,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, false);
using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false);
decompressor.Decompress(stream, 0, (uint)stream.Length, buffer);
Assert.Equal(data, buffer);

11
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -197,6 +197,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.DebugSave(provider);
}
[Theory]
[WithFile(Flower32BitGrayPredictorBigEndian, PixelTypes.Rgba32)]
[WithFile(Flower32BitGrayPredictorLittleEndian, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_32Bit_Gray_WithPredictor<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// Note: because the MagickReferenceDecoder fails to load the image, we only debug save them.
using Image<TPixel> image = provider.GetImage();
image.DebugSave(provider);
}
[Theory]
[WithFile(FlowerRgb121212Contiguous, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_36Bit<TPixel>(TestImageProvider<TPixel> provider)

2
tests/ImageSharp.Tests/TestImages.cs

@ -607,6 +607,8 @@ namespace SixLabors.ImageSharp.Tests
public const string Flower32BitGrayLittleEndian = "Tiff/flower-minisblack-32_lsb.tiff";
public const string Flower32BitGrayMinIsWhite = "Tiff/flower-miniswhite-32.tiff";
public const string Flower32BitGrayMinIsWhiteLittleEndian = "Tiff/flower-miniswhite-32_lsb.tiff";
public const string Flower32BitGrayPredictorBigEndian = "Tiff/flower-minisblack-32_msb_deflate_predictor.tiff";
public const string Flower32BitGrayPredictorLittleEndian = "Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff";
public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff";

3
tests/Images/Input/Tiff/flower-minisblack-32_lsb_deflate_predictor.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5e7998cc985ef11ab9da410f18dcfb6b9a3169fb1ec01f9e61aa38d8ee4cfb6
size 12704

3
tests/Images/Input/Tiff/flower-minisblack-32_msb_deflate_predictor.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:630e7f46655b6e61c4de7d56946a3a9225db68f776f9062ff2d5372547cc7c02
size 12704
Loading…
Cancel
Save