Browse Source

Allow encoding 4bit color palette images

pull/1570/head
Brian Popow 5 years ago
parent
commit
1dbe583824
  1. 5
      src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs
  2. 2
      src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs
  3. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
  4. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs
  5. 7
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs
  6. 10
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs
  7. 8
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs
  8. 9
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs
  9. 4
      src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs
  10. 2
      src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs
  11. 10
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  12. 20
      src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
  13. 5
      src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
  14. 27
      src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
  15. 4
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  16. 3
      src/ImageSharp/Formats/Tiff/TiffEncoder.cs
  17. 107
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  18. 22
      src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
  19. 8
      src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
  20. 5
      src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
  21. 76
      src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
  22. 2
      tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs
  23. 2
      tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs
  24. 38
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  25. 1
      tests/ImageSharp.Tests/TestImages.cs
  26. 3
      tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff

5
src/ImageSharp/Formats/Tiff/Compression/Compressors/NoCompressor.cs

@ -3,13 +3,14 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
internal class NoCompressor : TiffBaseCompressor
{
public NoCompressor(Stream output)
: base(output, default, default, default)
public NoCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel)
: base(output, memoryAllocator, width, bitsPerPixel)
{
}

2
src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs

@ -201,8 +201,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
this.useModifiedHuffman = useModifiedHuffman;
}
/// <inheritdoc/>
public override TiffEncoderCompression Method => this.useModifiedHuffman ? TiffEncoderCompression.ModifiedHuffman : TiffEncoderCompression.CcittGroup3Fax;
/// <inheritdoc/>
public override void Initialize(int rowsPerStrip)
{
// This is too much memory allocated, but just 1 bit per pixel will not do, if the compression rate is not good.

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// <remarks>
/// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type.
/// </remarks>
internal class DeflateTiffCompression : TiffBaseDecompresor
internal class DeflateTiffCompression : TiffBaseDecompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="DeflateTiffCompression" /> class.

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

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// <summary>
/// Class to handle cases where TIFF image data is compressed using LZW compression.
/// </summary>
internal class LzwTiffCompression : TiffBaseDecompresor
internal class LzwTiffCompression : TiffBaseDecompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="LzwTiffCompression" /> class.

7
src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs

@ -22,10 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// Initializes a new instance of the <see cref="ModifiedHuffmanTiffCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="photometricInterpretation">The photometric interpretation.</param>
/// <param name="width">The image width.</param>
public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width)
: base(allocator, FaxCompressionOptions.None, photometricInterpretation, width)
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
/// <param name="photometricInterpretation">The photometric interpretation.</param>
public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation)
: base(allocator, width, bitsPerPixel, FaxCompressionOptions.None, photometricInterpretation)
{
bool isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero;
this.whiteValue = (byte)(isWhiteZero ? 0 : 1);

10
src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs

@ -4,19 +4,23 @@
using System;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors
{
/// <summary>
/// Class to handle cases where TIFF image data is not compressed.
/// </summary>
internal class NoneTiffCompression : TiffBaseDecompresor
internal class NoneTiffCompression : TiffBaseDecompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="NoneTiffCompression" /> class.
/// </summary>
public NoneTiffCompression()
: base(default, default, default)
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The width of the image.</param>
/// <param name="bitsPerPixel">The bits per pixel.</param>
public NoneTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel)
: base(memoryAllocator, width, bitsPerPixel)
{
}

8
src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// <summary>
/// Class to handle cases where TIFF image data is compressed using PackBits compression.
/// </summary>
internal class PackBitsTiffCompression : TiffBaseDecompresor
internal class PackBitsTiffCompression : TiffBaseDecompressor
{
private IMemoryOwner<byte> compressedDataMemory;
@ -20,8 +20,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// Initializes a new instance of the <see cref="PackBitsTiffCompression" /> class.
/// </summary>
/// <param name="memoryAllocator">The memoryAllocator to use for buffer allocations.</param>
public PackBitsTiffCompression(MemoryAllocator memoryAllocator)
: base(memoryAllocator, default, default)
/// <param name="width">The width of the image.</param>
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
public PackBitsTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel)
: base(memoryAllocator, width, bitsPerPixel)
{
}

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

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// <summary>
/// Class to handle cases where TIFF image data is compressed using CCITT T4 compression.
/// </summary>
internal class T4TiffCompression : TiffBaseDecompresor
internal class T4TiffCompression : TiffBaseDecompressor
{
private readonly FaxCompressionOptions faxCompressionOptions;
@ -24,11 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompresso
/// Initializes a new instance of the <see cref="T4TiffCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="width">The image width.</param>
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
/// <param name="faxOptions">Fax compression options.</param>
/// <param name="photometricInterpretation">The photometric interpretation.</param>
/// <param name="width">The image width.</param>
public T4TiffCompression(MemoryAllocator allocator, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation, int width)
: base(allocator, width, default)
public T4TiffCompression(MemoryAllocator allocator, int width, int bitsPerPixel, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation)
: base(allocator, width, bitsPerPixel)
{
this.faxCompressionOptions = faxOptions;

4
src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompresor.cs → src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs

@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
/// <summary>
/// The base tiff decompressor class.
/// </summary>
internal abstract class TiffBaseDecompresor : TiffBaseCompression
internal abstract class TiffBaseDecompressor : TiffBaseCompression
{
protected TiffBaseDecompresor(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None)
protected TiffBaseDecompressor(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None)
: base(allocator, width, bitsPerPixel, predictor)
{
}

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

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set");
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new NoCompressor(output);
return new NoCompressor(output, allocator, width, bitsPerPixel);
case TiffEncoderCompression.PackBits:
DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set");

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

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
{
internal static class TiffDecompressorsFactory
{
public static TiffBaseDecompresor Create(
public static TiffBaseDecompressor Create(
TiffDecoderCompressionType method,
MemoryAllocator allocator,
TiffPhotometricInterpretation photometricInterpretation,
@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
case TiffDecoderCompressionType.None:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");
return new NoneTiffCompression();
return new NoneTiffCompression(allocator, width, bitsPerPixel);
case TiffDecoderCompressionType.PackBits:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");
return new PackBitsTiffCompression(allocator);
return new PackBitsTiffCompression(allocator, width, bitsPerPixel);
case TiffDecoderCompressionType.Deflate:
DebugGuard.IsTrue(faxOptions == FaxCompressionOptions.None, "No fax compression options are expected");
@ -40,11 +40,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
case TiffDecoderCompressionType.T4:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new T4TiffCompression(allocator, faxOptions, photometricInterpretation, width);
return new T4TiffCompression(allocator, width, bitsPerPixel, faxOptions, photometricInterpretation);
case TiffDecoderCompressionType.HuffmanRle:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new ModifiedHuffmanTiffCompression(allocator, photometricInterpretation, width);
return new ModifiedHuffmanTiffCompression(allocator, width, bitsPerPixel, photometricInterpretation);
default:
throw TiffThrowHelper.NotSupportedDecompressor(nameof(method));

20
src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs

@ -75,6 +75,26 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants
/// </summary>
public const int SizeOfDouble = 8;
/// <summary>
/// The bits per sample for 1 bit bicolor images.
/// </summary>
public static readonly ushort[] BitsPerSample1Bit = { 1 };
/// <summary>
/// The bits per sample for images with a 4 color palette.
/// </summary>
public static readonly ushort[] BitsPerSample4Bit = { 4 };
/// <summary>
/// The bits per sample for 8 bit images.
/// </summary>
public static readonly ushort[] BitsPerSample8Bit = { 8 };
/// <summary>
/// The bits per sample for images with 8 bits for each color channel.
/// </summary>
public static readonly ushort[] BitsPerSampleRgb8Bit = { 8, 8, 8 };
/// <summary>
/// The list of mimetypes that equate to a tiff.
/// </summary>

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

@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// </summary>
internal interface ITiffEncoderOptions
{
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
TiffBitsPerPixel? BitsPerPixel { get; set; }
/// <summary>
/// Gets the compression type to use.
/// </summary>

27
src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs

@ -3,19 +3,12 @@
using System;
using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
namespace SixLabors.ImageSharp.Formats.Tiff
{
internal static class TiffBitsPerSampleExtensions
{
private static readonly ushort[] One = { 1 };
private static readonly ushort[] Four = { 4 };
private static readonly ushort[] Eight = { 8 };
private static readonly ushort[] Rgb888 = { 8, 8, 8 };
/// <summary>
/// Gets the bits per channel array for a given BitsPerSample value, e,g, for RGB888: [8, 8, 8]
/// </summary>
@ -26,13 +19,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (tiffBitsPerSample)
{
case TiffBitsPerSample.One:
return One;
return TiffConstants.BitsPerSample1Bit;
case TiffBitsPerSample.Four:
return Four;
return TiffConstants.BitsPerSample4Bit;
case TiffBitsPerSample.Eight:
return Eight;
return TiffConstants.BitsPerSample8Bit;
case TiffBitsPerSample.Rgb888:
return Rgb888;
return TiffConstants.BitsPerSampleRgb8Bit;
default:
TiffThrowHelper.ThrowNotSupported("The bits per pixels are not supported");
@ -50,7 +43,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (bitsPerSample.Length)
{
case 3:
if (bitsPerSample[0] == Rgb888[0] && bitsPerSample[1] == Rgb888[1] && bitsPerSample[2] == Rgb888[2])
if (bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] &&
bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2])
{
return TiffBitsPerSample.Rgb888;
}
@ -58,17 +53,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff
break;
case 1:
if (bitsPerSample[0] == One[0])
if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0])
{
return TiffBitsPerSample.One;
}
if (bitsPerSample[0] == Four[0])
if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0])
{
return TiffBitsPerSample.Four;
}
if (bitsPerSample[0] == Eight[0])
if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0])
{
return TiffBitsPerSample.Eight;
}

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

@ -250,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize);
}
using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
RgbPlanarTiffColor<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.CreatePlanar(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap);
@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Buffer2D<TPixel> pixels = frame.PixelBuffer;
using TiffBaseDecompresor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions);
TiffBaseColorDecoder<TPixel> colorDecoder = TiffColorDecoderFactory<TPixel>.Create(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap);

3
src/ImageSharp/Formats/Tiff/TiffEncoder.cs

@ -17,6 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// </summary>
public class TiffEncoder : IImageEncoder, ITiffEncoderOptions
{
/// <inheritdoc/>
public TiffBitsPerPixel? BitsPerPixel { get; set; }
/// <inheritdoc/>
public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None;

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

@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@ -40,11 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// </summary>
private Configuration configuration;
/// <summary>
/// The color depth, in number of bits per pixel.
/// </summary>
private TiffBitsPerPixel bitsPerPixel;
/// <summary>
/// The quantizer for creating color palette image.
/// </summary>
@ -70,6 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.memoryAllocator = memoryAllocator;
this.Mode = options.Mode;
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
this.BitsPerPixel = options.BitsPerPixel;
this.UseHorizontalPredictor = options.UseHorizontalPredictor;
this.CompressionType = options.Compression;
this.compressionLevel = options.CompressionLevel;
@ -82,9 +79,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
internal TiffPhotometricInterpretation PhotometricInterpretation { get; private set; }
/// <summary>
/// Gets the compression implementation to use when encoding the image.
/// Gets or sets the compression implementation to use when encoding the image.
/// </summary>
internal TiffEncoderCompression CompressionType { get; }
internal TiffEncoderCompression CompressionType { get; set; }
/// <summary>
/// Gets the encoding mode to use. RGB, RGB with color palette or gray.
@ -97,6 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// </summary>
internal bool UseHorizontalPredictor { get; }
/// <summary>
/// Gets the bits per pixel.
/// </summary>
internal TiffBitsPerPixel? BitsPerPixel { get; private set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
@ -111,8 +113,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
TiffMetadata tiffMetadata = metadata.GetTiffMetadata();
this.BitsPerPixel ??= tiffMetadata.BitsPerPixel;
this.SetMode(image);
this.SetMode(tiffMetadata);
this.SetBitsPerPixel();
this.SetPhotometricInterpretation();
using (var writer = new TiffStreamWriter(stream))
@ -155,20 +161,31 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
// Write the image bytes to the steam.
var imageDataStart = (uint)writer.Position;
using TiffBaseCompressor compressor = TiffCompressorFactory.Create(
this.CompressionType,
writer.BaseStream,
this.memoryAllocator,
image.Width,
(int)this.bitsPerPixel,
this.compressionLevel,
this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None);
using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create(this.Mode, image.Frames.RootFrame, this.quantizer, this.memoryAllocator, this.configuration, entriesCollector);
int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame, colorWriter.BytesPerRow);
colorWriter.Write(compressor, rowsPerStrip);
TiffBitsPerPixel? tiffBitsPerPixel = this.BitsPerPixel;
if (tiffBitsPerPixel != null)
{
using TiffBaseCompressor compressor = TiffCompressorFactory.Create(
this.CompressionType,
writer.BaseStream,
this.memoryAllocator,
image.Width,
(int)tiffBitsPerPixel,
this.compressionLevel,
this.UseHorizontalPredictor ? TiffPredictor.Horizontal : TiffPredictor.None);
using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create(
this.Mode,
image.Frames.RootFrame,
this.quantizer,
this.memoryAllocator,
this.configuration,
entriesCollector,
(int)tiffBitsPerPixel);
int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow);
colorWriter.Write(compressor, rowsPerStrip);
}
entriesCollector.ProcessImageFormat(this);
entriesCollector.ProcessGeneral(image);
@ -177,12 +194,21 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
long nextIfdMarker = this.WriteIfd(writer, entriesCollector.Entries);
}
private int CalcRowsPerStrip(ImageFrame image, int bytesPerRow)
/// <summary>
/// Calculates the number of rows written per strip.
/// </summary>
/// <param name="height">The height of the image.</param>
/// <param name="bytesPerRow">The number of bytes per row.</param>
/// <returns>Number of rows per strip.</returns>
private int CalcRowsPerStrip(int height, int bytesPerRow)
{
int sz = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize;
int height = sz / bytesPerRow;
DebugGuard.MustBeGreaterThan(height, 0, nameof(height));
DebugGuard.MustBeGreaterThan(bytesPerRow, 0, nameof(bytesPerRow));
int stripBytes = this.maxStripBytes > 0 ? this.maxStripBytes : DefaultStripSize;
int rowsPerStrip = stripBytes / bytesPerRow;
return height > 0 ? (height < image.Height ? height : image.Height) : 1;
return rowsPerStrip > 0 ? (rowsPerStrip < height ? rowsPerStrip : height) : 1;
}
/// <summary>
@ -242,14 +268,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
return nextIfdMarker;
}
private void SetMode(Image image)
private void SetMode(TiffMetadata tiffMetadata)
{
// Make sure, that the fax compressions are only used together with the BiColor mode.
if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax || this.CompressionType == TiffEncoderCompression.ModifiedHuffman)
{
// Default means the user has not specified a preferred encoding mode.
if (this.Mode == TiffEncodingMode.Default)
{
this.Mode = TiffEncodingMode.BiColor;
this.bitsPerPixel = TiffBitsPerPixel.Pixel1;
return;
}
@ -262,36 +289,48 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
if (this.Mode == TiffEncodingMode.Default)
{
// Preserve input bits per pixel, if no mode was specified.
TiffMetadata tiffMetadata = image.Metadata.GetTiffMetadata();
switch (tiffMetadata.BitsPerPixel)
{
case TiffBitsPerPixel.Pixel1:
this.Mode = TiffEncodingMode.BiColor;
break;
case TiffBitsPerPixel.Pixel4:
this.Mode = TiffEncodingMode.ColorPalette;
break;
case TiffBitsPerPixel.Pixel8:
this.Mode = tiffMetadata.PhotometricInterpretation != TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.Gray : TiffEncodingMode.Rgb;
this.Mode = tiffMetadata.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ? TiffEncodingMode.ColorPalette : TiffEncodingMode.Gray;
break;
default:
this.Mode = TiffEncodingMode.Rgb;
break;
}
}
}
private void SetBitsPerPixel()
{
switch (this.Mode)
{
case TiffEncodingMode.BiColor:
this.bitsPerPixel = TiffBitsPerPixel.Pixel1;
this.BitsPerPixel = TiffBitsPerPixel.Pixel1;
break;
case TiffEncodingMode.ColorPalette:
if (this.BitsPerPixel != TiffBitsPerPixel.Pixel8 && this.BitsPerPixel != TiffBitsPerPixel.Pixel4)
{
this.BitsPerPixel = TiffBitsPerPixel.Pixel8;
}
break;
case TiffEncodingMode.Gray:
this.bitsPerPixel = TiffBitsPerPixel.Pixel8;
this.BitsPerPixel = TiffBitsPerPixel.Pixel8;
break;
case TiffEncodingMode.Rgb:
this.bitsPerPixel = TiffBitsPerPixel.Pixel24;
this.BitsPerPixel = TiffBitsPerPixel.Pixel24;
break;
default:
this.Mode = TiffEncodingMode.Rgb;
this.bitsPerPixel = TiffBitsPerPixel.Pixel24;
this.BitsPerPixel = TiffBitsPerPixel.Pixel24;
break;
}
}

22
src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs

@ -298,25 +298,33 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
switch (encoder.PhotometricInterpretation)
{
case TiffPhotometricInterpretation.PaletteColor:
return new ushort[] { 8 };
if (encoder.BitsPerPixel == TiffBitsPerPixel.Pixel4)
{
return TiffConstants.BitsPerSample4Bit;
}
else
{
return TiffConstants.BitsPerSample8Bit;
}
case TiffPhotometricInterpretation.Rgb:
return new ushort[] { 8, 8, 8 };
return TiffConstants.BitsPerSampleRgb8Bit;
case TiffPhotometricInterpretation.WhiteIsZero:
if (encoder.Mode == TiffEncodingMode.BiColor)
{
return new ushort[] { 1 };
return TiffConstants.BitsPerSample1Bit;
}
return new ushort[] { 8 };
return TiffConstants.BitsPerSample8Bit;
case TiffPhotometricInterpretation.BlackIsZero:
if (encoder.Mode == TiffEncodingMode.BiColor)
{
return new ushort[] { 1 };
return TiffConstants.BitsPerSample1Bit;
}
return new ushort[] { 8 };
return TiffConstants.BitsPerSample8Bit;
default:
return new ushort[] { 8, 8, 8 };
return TiffConstants.BitsPerSampleRgb8Bit;
}
}

8
src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs

@ -20,13 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
this.MemoryAllocator = memoryAllocator;
this.Configuration = configuration;
this.EntriesCollector = entriesCollector;
this.BytesPerRow = ((image.Width * this.BitsPerPixel) + 7) / 8;
}
public abstract int BitsPerPixel { get; }
public int BytesPerRow { get; }
public int BytesPerRow => ((this.Image.Width * this.BitsPerPixel) + 7) / 8;
protected ImageFrame<TPixel> Image { get; }
@ -38,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
public virtual void Write(TiffBaseCompressor compressor, int rowsPerStrip)
{
DebugGuard.IsTrue(this.BytesPerRow == compressor.BytesPerRow || compressor.BytesPerRow == 0, "Values must be equals");
DebugGuard.IsTrue(this.BytesPerRow == compressor.BytesPerRow, "bytes per row of the compressor does not match tiff color writer");
int stripsCount = (this.Image.Height + rowsPerStrip - 1) / rowsPerStrip;
uint[] stripOffsets = new uint[stripsCount];
@ -59,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
stripIndex++;
}
DebugGuard.IsTrue(stripIndex == stripsCount, "Values must be equals");
DebugGuard.IsTrue(stripIndex == stripsCount, "stripIndex and stripsCount should match");
this.AddStripTags(rowsPerStrip, stripOffsets, stripByteCounts);
}

5
src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs

@ -15,13 +15,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
IQuantizer quantizer,
MemoryAllocator memoryAllocator,
Configuration configuration,
TiffEncoderEntriesCollector entriesCollector)
TiffEncoderEntriesCollector entriesCollector,
int bitsPerPixel)
where TPixel : unmanaged, IPixel<TPixel>
{
switch (mode)
{
case TiffEncodingMode.ColorPalette:
return new TiffPaletteWriter<TPixel>(image, quantizer, memoryAllocator, configuration, entriesCollector);
return new TiffPaletteWriter<TPixel>(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffEncodingMode.Gray:
return new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
case TiffEncodingMode.BiColor:

76
src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs

@ -4,6 +4,7 @@
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
using SixLabors.ImageSharp.Memory;
@ -16,52 +17,85 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
internal sealed class TiffPaletteWriter<TPixel> : TiffBaseColorWriter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private const int ColorsPerChannel = 256;
private const int ColorPaletteSize = ColorsPerChannel * 3;
private const int ColorPaletteBytes = ColorPaletteSize * 2;
private readonly IndexedImageFrame<TPixel> quantized;
public TiffPaletteWriter(ImageFrame<TPixel> image, IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
private readonly int maxColors;
private readonly int colorPaletteSize;
private readonly int colorPaletteBytes;
private readonly IndexedImageFrame<TPixel> quantizedImage;
public TiffPaletteWriter(
ImageFrame<TPixel> image,
IQuantizer quantizer,
MemoryAllocator memoryAllocator,
Configuration configuration,
TiffEncoderEntriesCollector entriesCollector,
int bitsPerPixel)
: base(image, memoryAllocator, configuration, entriesCollector)
{
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.Configuration);
this.quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());
DebugGuard.NotNull(quantizer, nameof(quantizer));
DebugGuard.NotNull(configuration, nameof(configuration));
DebugGuard.NotNull(entriesCollector, nameof(entriesCollector));
DebugGuard.MustBeBetweenOrEqualTo(bitsPerPixel, 4, 8, nameof(bitsPerPixel));
this.BitsPerPixel = bitsPerPixel;
this.maxColors = this.BitsPerPixel == 4 ? 16 : 256;
this.colorPaletteSize = this.maxColors * 3;
this.colorPaletteBytes = this.colorPaletteSize * 2;
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.Configuration, new QuantizerOptions()
{
MaxColors = this.maxColors
});
this.quantizedImage = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());
this.AddTag(this.quantized);
this.AddColorMapTag();
}
/// <inheritdoc />
public override int BitsPerPixel => 8;
public override int BitsPerPixel { get; }
/// <inheritdoc />
protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor)
{
Span<byte> pixels = GetStripPixels(((IPixelSource)this.quantized).PixelBuffer, y, height);
compressor.CompressStrip(pixels, height);
Span<byte> pixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height);
if (this.BitsPerPixel == 4)
{
using IMemoryOwner<byte> rows4bitBuffer = this.MemoryAllocator.Allocate<byte>(pixels.Length / 2);
Span<byte> rows4bit = rows4bitBuffer.GetSpan();
int idx = 0;
for (int i = 0; i < rows4bit.Length; i++)
{
rows4bit[i] = (byte)((pixels[idx] << 4) | (pixels[idx + 1] & 0xF));
idx += 2;
}
compressor.CompressStrip(rows4bit, height);
}
else
{
compressor.CompressStrip(pixels, height);
}
}
/// <inheritdoc />
protected override void Dispose(bool disposing) => this.quantized?.Dispose();
protected override void Dispose(bool disposing) => this.quantizedImage?.Dispose();
private void AddTag(IndexedImageFrame<TPixel> quantized)
private void AddColorMapTag()
{
using IMemoryOwner<byte> colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(ColorPaletteBytes);
using IMemoryOwner<byte> colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(this.colorPaletteBytes);
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColors = quantized.Palette.Span;
ReadOnlySpan<TPixel> quantizedColors = this.quantizedImage.Palette.Span;
int quantizedColorBytes = quantizedColors.Length * 3 * 2;
// In the ColorMap, black is represented by 0,0,0 and white is represented by 65535, 65535, 65535.
// In the ColorMap, black is represented by 0, 0, 0 and white is represented by 65535, 65535, 65535.
Span<Rgb48> quantizedColorRgb48 = MemoryMarshal.Cast<byte, Rgb48>(colorPalette.Slice(0, quantizedColorBytes));
PixelOperations<TPixel>.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48);
// It can happen that the quantized colors are less than the expected 256 per channel.
var diffToMaxColors = ColorsPerChannel - quantizedColors.Length;
// It can happen that the quantized colors are less than the expected maximum per channel.
var diffToMaxColors = this.maxColors - quantizedColors.Length;
// In a TIFF ColorMap, all the Red values come first, followed by the Green values,
// then the Blue values. Convert the quantized palette to this format.
var palette = new ushort[ColorPaletteSize];
var palette = new ushort[this.colorPaletteSize];
int paletteIdx = 0;
for (int i = 0; i < quantizedColors.Length; i++)
{

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

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData));
var buffer = new byte[expectedResult.Length];
new NoneTiffCompression().Decompress(stream, 0, byteCount, buffer);
new NoneTiffCompression(default, default, default).Decompress(stream, 0, byteCount, buffer);
Assert.Equal(expectedResult, buffer);
}

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

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
var stream = new BufferedReadStream(Configuration.Default, new MemoryStream(inputData));
var buffer = new byte[expectedResult.Length];
using var decompressor = new PackBitsTiffCompression(new ArrayPoolMemoryAllocator());
using var decompressor = new PackBitsTiffCompression(new ArrayPoolMemoryAllocator(), default, default);
decompressor.Decompress(stream, 0, (uint)inputData.Length, buffer);
Assert.Equal(expectedResult, buffer);

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
@ -38,7 +37,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.None, TiffBitsPerPixel.Pixel1, TiffCompression.None)]
[InlineData(TiffEncodingMode.Default, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel24, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel8, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.Deflate, TiffBitsPerPixel.Pixel1, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.Default, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel24, TiffCompression.PackBits)]
@ -47,7 +45,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel8, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.PackBits, TiffBitsPerPixel.Pixel1, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.Rgb, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel24, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.Gray, TiffEncoderCompression.Lzw, TiffBitsPerPixel.Pixel8, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.CcittGroup3Fax, TiffBitsPerPixel.Pixel1, TiffCompression.CcittGroup3Fax)]
[InlineData(TiffEncodingMode.BiColor, TiffEncoderCompression.ModifiedHuffman, TiffBitsPerPixel.Pixel1, TiffCompression.Ccitt1D)]
@ -73,7 +70,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel1)]
[WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel24)]
[WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel4)]
[WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Pixel8)]
public void TiffEncoder_PreserveBitsPerPixel<TPixel>(TestImageProvider<TPixel> provider, TiffBitsPerPixel expectedBitsPerPixel)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)]
[WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)]
[WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)]
public void TiffEncoder_CorrectBiMode<TPixel>(TestImageProvider<TPixel> provider, TiffEncoderCompression compression, TiffCompression expectedCompression)
public void TiffEncoder_EncodesWithCorrectBiColorModeCompression<TPixel>(TestImageProvider<TPixel> provider, TiffEncoderCompression compression, TiffCompression expectedCompression)
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
@ -197,37 +196,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
[WithFile(Rgb4BitPalette, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_With4Bit_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Lzw, usePredictor: true, useExactComparer: false, compareTolerance: 0.001f);
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f);
TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.ColorPalette, TiffEncoderCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f);
[Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
@ -335,6 +316,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var encoder = new TiffEncoder
{
Mode = mode,
BitsPerPixel = bitsPerPixel,
Compression = compression,
UseHorizontalPredictor = usePredictor,
MaxStripBytes = maxStripSize

1
tests/ImageSharp.Tests/TestImages.cs

@ -556,6 +556,7 @@ namespace SixLabors.ImageSharp.Tests
public const string RgbPackbitsMultistrip = "Tiff/rgb_packbits_multistrip.tiff";
public const string RgbUncompressed = "Tiff/rgb_uncompressed.tiff";
public const string RgbPalette = "Tiff/rgb_palette.tiff";
public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff";
public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff";
public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff";

3
tests/Images/Input/Tiff/bike_colorpalette_4bit.tiff

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