diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 4d7bbbeb7..1a81f2286 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -40,7 +40,7 @@ | |Encoder|Decoder|Comments | |---------------------------|:-----:|:-----:|--------------------------| -|None | Y | Y | encoding only rgb so far | +|None | Y | Y | | |Ccitt1D | | Y | | |PackBits | | Y | | |CcittGroup3Fax | | Y | | @@ -48,7 +48,7 @@ |Lzw | | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case | |Old Jpeg | | | We should not even try to support this | |Jpeg (Technote 2) | | | | -|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? | +|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. | |Old Deflate (Technote 2) | | Y | | ### Photometric Interpretation Formats @@ -75,7 +75,7 @@ |ImageWidth | Y | Y | | |ImageLength | Y | Y | | |BitsPerSample | Y | Y | | -|Compression | | Y | | +|Compression | Y | Y | | |PhotometricInterpretation | Y | Y | | |Thresholding | | | | |CellWidth | | | | diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index f070eab31..7cf12afb5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, out colorMap); break; case TiffEncodingMode.Gray: imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); @@ -389,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff return (ushort)TiffCompression.Deflate; } + if (this.CompressionType == TiffEncoderCompression.Deflate && + this.Mode == TiffEncodingMode.ColorPalette) + { + return (ushort)TiffCompression.Deflate; + } + return (ushort)TiffCompression.None; } } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 028d53ab8..2c6e03d9c 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -201,10 +201,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The padding bytes for each row. /// The color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorPaletteSize = 256 * 3 * 2; + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); @@ -253,16 +255,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff for (int y = 0; y < image.Height; y++) { ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - this.output.Write(pixelSpan); - bytesWritten += pixelSpan.Length; + + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Write(pixelSpan); + } + else + { + // No compression. + this.output.Write(pixelSpan); + bytesWritten += pixelSpan.Length; + } for (int i = 0; i < padding; i++) { - this.output.WriteByte(0); - bytesWritten++; + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.WriteByte(0); + } + else + { + // no compression. + this.output.WriteByte(0); + bytesWritten++; + } } } + if (compression == TiffEncoderCompression.Deflate) + { + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.output.Write(buffer); + bytesWritten += buffer.Length; + } + return bytesWritten; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 458acd34c..9c043b4ee 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -71,6 +71,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); + // 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)] + public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate); + private static void TestTiffEncoderCore( TestImageProvider provider, TiffBitsPerPixel bitsPerPixel,