Browse Source

Add support for encoding packed bits compressed tiff's

pull/1570/head
Brian Popow 5 years ago
parent
commit
d3a8f5ef69
  1. 1
      src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs
  2. 1
      src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs
  3. 1
      src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs
  4. 2
      src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs
  5. 2
      src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs
  6. 128
      src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs
  7. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs
  8. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs
  9. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs
  10. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs
  11. 1
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs
  12. 5
      src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
  13. 18
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  14. 2
      src/ImageSharp/Formats/Tiff/Utils/BitReader.cs
  15. 4
      src/ImageSharp/Formats/Tiff/Utils/SubStream.cs
  16. 2
      src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs
  17. 6
      src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs
  18. 2
      src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
  19. 131
      src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
  20. 2
      tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
  21. 16
      tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
  22. 2
      tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs
  23. 28
      tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs
  24. 3
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
  25. 23
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  26. 4
      tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs
  27. 4
      tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs

1
src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs

@ -4,6 +4,7 @@
using System;
using System.IO;
using System.IO.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression

1
src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs

@ -3,6 +3,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression

1
src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs

@ -3,6 +3,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression

2
src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs

@ -3,6 +3,8 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression

2
src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs

@ -4,6 +4,8 @@
using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression

128
src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs

@ -0,0 +1,128 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{
/// <summary>
/// Pack Bits compression for tiff images. See Tiff Spec v6, section 9.
/// </summary>
internal static class PackBitsWriter
{
public static int PackBits(Span<byte> rowSpan, Span<byte> compressedRowSpan)
{
int maxRunLength = 127;
int posInRowSpan = 0;
int bytesWritten = 0;
int literalRunLength = 0;
while (posInRowSpan < rowSpan.Length)
{
var useReplicateRun = IsReplicateRun(rowSpan, posInRowSpan);
if (useReplicateRun)
{
if (literalRunLength > 0)
{
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten);
bytesWritten += literalRunLength + 1;
}
// Write a run with the same bytes.
var runLength = FindRunLength(rowSpan, posInRowSpan, maxRunLength);
WriteRun(rowSpan, posInRowSpan, runLength, compressedRowSpan, bytesWritten);
bytesWritten += 2;
literalRunLength = 0;
posInRowSpan += runLength;
continue;
}
literalRunLength++;
posInRowSpan++;
if (literalRunLength >= maxRunLength)
{
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten);
bytesWritten += literalRunLength + 1;
literalRunLength = 0;
}
}
if (literalRunLength > 0)
{
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten);
bytesWritten += literalRunLength + 1;
}
return bytesWritten;
}
private static void WriteLiteralRun(Span<byte> rowSpan, int end, int literalRunLength, Span<byte> compressedRowSpan, int compressedRowPos)
{
DebugGuard.MustBeLessThanOrEqualTo(literalRunLength, 127, nameof(literalRunLength));
int literalRunStart = end - literalRunLength;
sbyte runLength = (sbyte)(literalRunLength - 1);
compressedRowSpan[compressedRowPos] = (byte)runLength;
rowSpan.Slice(literalRunStart, literalRunLength).CopyTo(compressedRowSpan.Slice(compressedRowPos + 1));
}
private static void WriteRun(Span<byte> rowSpan, int start, int runLength, Span<byte> compressedRowSpan, int compressedRowPos)
{
DebugGuard.MustBeLessThanOrEqualTo(runLength, 127, nameof(runLength));
sbyte headerByte = (sbyte)(-runLength + 1);
compressedRowSpan[compressedRowPos] = (byte)headerByte;
compressedRowSpan[compressedRowPos + 1] = rowSpan[start];
}
private static bool IsReplicateRun(Span<byte> rowSpan, int startPos)
{
// We consider run which has at least 3 same consecutive bytes a candidate for a run.
var startByte = rowSpan[startPos];
int count = 0;
for (int i = startPos + 1; i < rowSpan.Length; i++)
{
if (rowSpan[i] == startByte)
{
count++;
if (count >= 2)
{
return true;
}
}
else
{
break;
}
}
return false;
}
private static int FindRunLength(Span<byte> rowSpan, int startPos, int maxRunLength)
{
var startByte = rowSpan[startPos];
int count = 1;
for (int i = startPos + 1; i < rowSpan.Length; i++)
{
if (rowSpan[i] == startByte)
{
count++;
}
else
{
break;
}
if (count == maxRunLength)
{
break;
}
}
return count;
}
}
}

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor.cs

@ -3,6 +3,7 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor.cs

@ -3,6 +3,7 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor.cs

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

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor.cs

@ -3,6 +3,7 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

1
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor.cs

@ -3,6 +3,7 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;

5
src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs

@ -18,6 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
Deflate,
/// <summary>
/// Use PackBits to compression the image data.
/// </summary>
PackBits,
/// <summary>
/// Use CCITT T4 1D compression. Note: This is only valid for bi-level images.
/// </summary>

18
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -7,6 +7,7 @@ using System.IO;
using System.Threading;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -166,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
imageDataBytes = writer.WriteBiColor(image, this.CompressionType);
break;
default:
imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType);
imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType);
break;
}
@ -415,11 +416,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return (ushort)TiffCompression.Deflate;
}
if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Rgb)
{
return (ushort)TiffCompression.PackBits;
}
if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.Gray)
{
return (ushort)TiffCompression.Deflate;
}
if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.Gray)
{
return (ushort)TiffCompression.PackBits;
}
if (this.CompressionType == TiffEncoderCompression.Deflate && this.Mode == TiffEncodingMode.ColorPalette)
{
return (ushort)TiffCompression.Deflate;
@ -430,6 +441,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return (ushort)TiffCompression.Deflate;
}
if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.BiColor)
{
return (ushort)TiffCompression.PackBits;
}
if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax && this.Mode == TiffEncodingMode.BiColor)
{
return (ushort)TiffCompression.CcittGroup3Fax;

2
src/ImageSharp/Formats/Tiff/Utils/BitReader.cs

@ -3,7 +3,7 @@
using System;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Utility class to read a sequence of bits from an array

4
src/ImageSharp/Formats/Tiff/Utils/SubStream.cs

@ -4,7 +4,7 @@
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Utility class to encapsulate a sub-portion of another <see cref="Stream"/>.
@ -173,4 +173,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff
throw new NotSupportedException();
}
}
}
}

2
src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs

@ -7,7 +7,7 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Decompresses and decodes data using the dynamic LZW algorithms.

6
src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -6,7 +6,7 @@ using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Formats.Gif;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Encodes and compresses the image data using dynamic Lempel-Ziv compression.
@ -492,4 +492,4 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.isDisposed = true;
}
}
}
}

2
src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs

@ -4,7 +4,7 @@
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// TIFF specific utilities and extension methods.

131
src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs

@ -3,7 +3,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
@ -17,7 +16,7 @@ using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Tiff
namespace SixLabors.ImageSharp.Formats.Tiff.Utils
{
/// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>.
@ -32,8 +31,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private readonly byte[] paddingBytes = new byte[4];
private readonly List<long> references = new List<long>();
/// <summary>
/// Initializes a new instance of the <see cref="TiffWriter"/> class.
/// </summary>
@ -141,7 +138,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <param name="padding">The padding bytes for each row.</param>
/// <param name="compression">The compression to use.</param>
/// <returns>The number of bytes written.</returns>
public int WriteRgbImageData<TPixel>(Image<TPixel> image, int padding, TiffEncoderCompression compression)
public int WriteRgb<TPixel>(Image<TPixel> image, int padding, TiffEncoderCompression compression)
where TPixel : unmanaged, IPixel<TPixel>
{
using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding);
@ -151,6 +148,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return this.WriteDeflateCompressedRgb(image, rowSpan);
}
if (compression == TiffEncoderCompression.PackBits)
{
return this.WriteRgbPackBitsCompressed(image, rowSpan);
}
// No compression.
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
@ -195,6 +197,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
/// <summary>
/// Writes the image data as RGB with packed bits compression to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param>
/// <param name="rowSpan">A Span for a pixel row.</param>
/// <returns>The number of bytes written.</returns>
private int WriteRgbPackBitsCompressed<TPixel>(Image<TPixel> image, Span<byte> rowSpan)
where TPixel : unmanaged, IPixel<TPixel>
{
// Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes.
int additionalBytes = ((image.Width * 3) / 127) + 1;
using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean);
Span<byte> compressedRowSpan = compressedRow.GetSpan();
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
Span<TPixel> pixelRow = image.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan);
this.output.Write(compressedRow.Slice(0, size));
bytesWritten += size;
compressedRowSpan.Clear();
}
return bytesWritten;
}
/// <summary>
/// Writes the image data as indices into a color map to the stream.
/// </summary>
@ -316,6 +347,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return this.WriteGrayDeflateCompressed(image, rowSpan);
}
if (compression == TiffEncoderCompression.PackBits)
{
return this.WriteGrayPackBitsCompressed(image, rowSpan);
}
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
@ -358,6 +394,35 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
/// <summary>
/// Writes the image data as 8 bit gray to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param>
/// <param name="rowSpan">A span of a row of pixels.</param>
/// <returns>The number of bytes written.</returns>
private int WriteGrayPackBitsCompressed<TPixel>(Image<TPixel> image, Span<byte> rowSpan)
where TPixel : unmanaged, IPixel<TPixel>
{
// Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes.
int additionalBytes = (image.Width / 127) + 1;
using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(image.Width + additionalBytes, AllocationOptions.Clean);
Span<byte> compressedRowSpan = compressedRow.GetSpan();
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
Span<TPixel> pixelRow = image.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan);
this.output.Write(compressedRow.Slice(0, size));
bytesWritten += size;
compressedRowSpan.Clear();
}
return bytesWritten;
}
/// <summary>
/// Writes the image data as 1 bit black and white to the stream.
/// </summary>
@ -385,6 +450,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow);
}
if (compression == TiffEncoderCompression.PackBits)
{
return this.WriteBiColorPackBits(imageBlackWhite, pixelRowAsGraySpan, outputRow);
}
if (compression == TiffEncoderCompression.CcittGroup3Fax)
{
var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration);
@ -397,6 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output);
}
// Write image uncompressed.
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
@ -479,6 +550,56 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
/// <summary>
/// Writes the image data as 1 bit black and white with pack bits compression to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param>
/// <param name="pixelRowAsGraySpan">A span for converting a pixel row to gray.</param>
/// <param name="outputRow">A span which will be used to store the output pixels.</param>
/// <returns>The number of bytes written.</returns>
public int WriteBiColorPackBits<TPixel>(Image<TPixel> image, Span<L8> pixelRowAsGraySpan, Span<byte> outputRow)
where TPixel : unmanaged, IPixel<TPixel>
{
// Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits.
int additionalBytes = (image.Width / 127) + 1;
int compressedRowBytes = (image.Width / 8) + additionalBytes;
using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean);
Span<byte> compressedRowSpan = compressedRow.GetSpan();
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
int bitIndex = 0;
int byteIndex = 0;
Span<TPixel> pixelRow = image.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan);
for (int x = 0; x < pixelRow.Length; x++)
{
int shift = 7 - bitIndex;
if (pixelRowAsGraySpan[x].PackedValue == 255)
{
outputRow[byteIndex] |= (byte)(1 << shift);
}
bitIndex++;
if (bitIndex == 8)
{
byteIndex++;
bitIndex = 0;
}
}
var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan);
this.output.Write(compressedRowSpan);
bytesWritten += size;
outputRow.Clear();
}
return bytesWritten;
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding);
/// <summary>

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

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
{
[Trait("Category", "Tiff")]
public class DeflateTiffCompressionTests

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

@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
{
[Trait("Category", "Tiff")]
public class LzwTiffCompressionTests
@ -19,14 +21,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence
public void Decompress_ReadsData(byte[] data)
{
using (Stream stream = CreateCompressedStream(data))
{
var buffer = new byte[data.Length];
using Stream stream = CreateCompressedStream(data);
var buffer = new byte[data.Length];
new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer);
new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer);
Assert.Equal(data, buffer);
}
Assert.Equal(data, buffer);
}
private static Stream CreateCompressedStream(byte[] data)

2
tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs

@ -5,7 +5,7 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
{
[Trait("Category", "Tiff")]
public class NoneTiffCompressionTests

28
tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs

@ -1,13 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
{
[Trait("Category", "Tiff")]
public class PackBitsTiffCompressionTests
@ -20,16 +22,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Formats.Tiff
[InlineData(new byte[] { 0xFE, 0x2A }, new byte[] { 0x2A, 0x2A, 0x2A })] // Repeat three bytes
[InlineData(new byte[] { 0x80 }, new byte[] { })] // Read a 'No operation' byte
[InlineData(new byte[] { 0x01, 0x15, 0x32, 0x80, 0xFF, 0xA2 }, new byte[] { 0x15, 0x32, 0xA2, 0xA2 })] // Read two bytes, nop, repeat two bytes
[InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA },
new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample
[InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample
public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult)
{
Stream stream = new MemoryStream(inputData);
byte[] buffer = new byte[expectedResult.Length];
var buffer = new byte[expectedResult.Length];
new PackBitsTiffCompression(new ArrayPoolMemoryAllocator()).Decompress(stream, inputData.Length, buffer);
Assert.Equal(expectedResult, buffer);
}
[Theory]
[InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0xCD, 0xBB, 0xBB, 0xBB, 0xBB }, new byte[] { 0xFE, 0xAA, 0x02, 0xCD, 0xFD, 0xBB })] // A run of 3, then one byte, followed by a run of 4.
[InlineData(new byte[] { 0xAB, 0xCD, 0xEF }, new byte[] { 0x04, 0xAB, 0xCD, 0xEF })] // all bytes are different.
[InlineData(new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA })] // Apple PackBits sample
public void Compress_Works(byte[] inputData, byte[] expectedResult)
{
// arrange
Span<byte> input = inputData.AsSpan();
var compressed = new byte[expectedResult.Length];
// act
PackBitsWriter.PackBits(input, compressed);
// assert
Assert.Equal(expectedResult, compressed);
}
}
}

3
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs

@ -2,8 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff

23
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

@ -47,25 +47,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
}
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
[WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb);
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
[WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate);
[Theory]
[WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
[WithFile(TestImages.Tiff.Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.PackBits);
[Theory]
[WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray);
[Theory]
[WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
[WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate);
[Theory]
[WithFile(TestImages.Tiff.Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.PackBits);
// TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette.
[Theory]
[WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
@ -88,6 +98,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate);
[Theory]
[WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel1, TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits);
[Theory]
[WithFile(TestImages.Tiff.Calliphora_BiColor, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works<TPixel>(TestImageProvider<TPixel> provider)

4
tests/ImageSharp.Tests/Formats/Tiff/Utils/SubStreamTests.cs

@ -3,7 +3,9 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff

4
tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff

Loading…
Cancel
Save