Browse Source

Add option to use lzw with paletted image

pull/1467/head
Brian Popow 5 years ago
parent
commit
c5fa9fd454
  1. 5
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  2. 51
      src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
  3. 62
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

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

@ -463,6 +463,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
return (ushort)TiffCompression.Deflate;
}
if (this.CompressionType == TiffEncoderCompression.Lzw && this.Mode == TiffEncodingMode.ColorPalette)
{
return (ushort)TiffCompression.Lzw;
}
if (this.CompressionType == TiffEncoderCompression.PackBits && this.Mode == TiffEncodingMode.ColorPalette)
{
return (ushort)TiffCompression.PackBits;

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

@ -344,6 +344,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor);
}
if (compression == TiffEncoderCompression.Lzw)
{
return this.WriteLzwCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor);
}
if (compression == TiffEncoderCompression.PackBits)
{
return this.WritePackBitsCompressedPalettedRgb(image, quantized, padding);
@ -414,6 +419,52 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
return bytesWritten;
}
/// <summary>
/// Writes the image data as indices into a color map compressed with lzw 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="quantized">The quantized frame.</param>
/// <param name="padding">The padding bytes for each row.</param>
/// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used.</param>
/// <returns>The number of bytes written.</returns>
public int WriteLzwCompressedPalettedRgb<TPixel>(Image<TPixel> image, IndexedImageFrame<TPixel> quantized, int padding, bool useHorizontalPredictor)
where TPixel : unmanaged, IPixel<TPixel>
{
IMemoryOwner<byte> pixelData = this.memoryAllocator.Allocate<byte>(image.Width * image.Height);
using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width);
using var memoryStream = new MemoryStream();
int bytesWritten = 0;
Span<byte> pixels = pixelData.GetSpan();
for (int y = 0; y < image.Height; y++)
{
ReadOnlySpan<byte> indexedPixelRow = quantized.GetPixelRowSpan(y);
if (useHorizontalPredictor)
{
// We need a writable Span here.
Span<byte> pixelRowCopy = tmpBuffer.GetSpan();
indexedPixelRow.CopyTo(pixelRowCopy);
HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy);
pixelRowCopy.CopyTo(pixels.Slice(y * image.Width));
}
else
{
indexedPixelRow.CopyTo(pixels.Slice(y * image.Width));
}
}
using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8);
lzwEncoder.Encode(memoryStream);
byte[] buffer = memoryStream.ToArray();
this.output.Write(buffer);
bytesWritten += buffer.Length;
return bytesWritten;
}
/// <summary>
/// Writes the image data as indices into a color map compressed with deflate compression to the stream.
/// </summary>

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

@ -111,19 +111,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// Because a quantizer is used to create the palette (and therefore changes to the original are expected),
// we do not compare the encoded image against the original:
// Instead we load the encoded image with a reference decoder and compare against that image.
using Image<TPixel> image = provider.GetImage();
using var memStream = new MemoryStream();
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.None };
image.Save(memStream, encoder);
memStream.Position = 0;
using var encodedImage = (Image<TPixel>)Image.Load(memStream);
var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder);
TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage);
TiffEncoderPaletteTest(provider, encoder);
}
[Theory]
@ -131,16 +121,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
using var memStream = new MemoryStream();
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate };
image.Save(memStream, encoder);
memStream.Position = 0;
using var encodedImage = (Image<TPixel>)Image.Load(memStream);
var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder);
TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage);
TiffEncoderPaletteTest(provider, encoder);
}
[Theory]
@ -148,16 +131,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
using var memStream = new MemoryStream();
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true};
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Deflate, UseHorizontalPredictor = true };
image.Save(memStream, encoder);
memStream.Position = 0;
TiffEncoderPaletteTest(provider, encoder);
}
using var encodedImage = (Image<TPixel>)Image.Load(memStream);
var encodedImagePath = provider.Utility.SaveTestOutputFile(encodedImage, "tiff", encoder);
TiffTestUtils.CompareWithReferenceDecoder(encodedImagePath, encodedImage);
[Theory]
[WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw };
TiffEncoderPaletteTest(provider, encoder);
}
[Theory]
[WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.Lzw, UseHorizontalPredictor = true };
TiffEncoderPaletteTest(provider, encoder);
}
[Theory]
@ -165,9 +161,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits };
TiffEncoderPaletteTest(provider, encoder);
}
private static void TiffEncoderPaletteTest<TPixel>(TestImageProvider<TPixel> provider, TiffEncoder encoder)
where TPixel : unmanaged, IPixel<TPixel>
{
// Because a quantizer is used to create the palette (and therefore changes to the original are expected),
// we do not compare the encoded image against the original:
// Instead we load the encoded image with a reference decoder and compare against that image.
using Image<TPixel> image = provider.GetImage();
using var memStream = new MemoryStream();
var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits };
image.Save(memStream, encoder);
memStream.Position = 0;

Loading…
Cancel
Save