diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index d64ac2e7d..93deca380 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/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;
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
index 53f763a02..db8bad133 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
+++ b/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;
}
+ ///
+ /// Writes the image data as indices into a color map compressed with lzw compression to the stream.
+ ///
+ /// The pixel data.
+ /// The image to write to the stream.
+ /// The quantized frame.
+ /// The padding bytes for each row.
+ /// Indicates if horizontal prediction should be used.
+ /// The number of bytes written.
+ public int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, bool useHorizontalPredictor)
+ where TPixel : unmanaged, IPixel
+ {
+ IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height);
+ using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width);
+ using var memoryStream = new MemoryStream();
+
+ int bytesWritten = 0;
+ Span pixels = pixelData.GetSpan();
+ for (int y = 0; y < image.Height; y++)
+ {
+ ReadOnlySpan indexedPixelRow = quantized.GetPixelRowSpan(y);
+
+ if (useHorizontalPredictor)
+ {
+ // We need a writable Span here.
+ Span 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;
+ }
+
///
/// Writes the image data as indices into a color map compressed with deflate compression to the stream.
///
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index aa5a84d83..2e6ca6318 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -111,19 +111,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- // 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 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)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(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- using Image 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)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(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- using Image 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)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(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ 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(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ 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(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
+ var encoder = new TiffEncoder { Mode = TiffEncodingMode.ColorPalette, Compression = TiffEncoderCompression.PackBits };
+
+ TiffEncoderPaletteTest(provider, encoder);
+ }
+
+ private static void TiffEncoderPaletteTest(TestImageProvider provider, TiffEncoder encoder)
+ where TPixel : unmanaged, IPixel
+ {
+ // 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 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;