diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md
index 2bacf7c51..4d7bbbeb7 100644
--- a/src/ImageSharp/Formats/Tiff/README.md
+++ b/src/ImageSharp/Formats/Tiff/README.md
@@ -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, should we allow this for the gray and palette too? |
+|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? |
|Old Deflate (Technote 2) | | Y | |
### Photometric Interpretation Formats
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 6dee09932..f070eab31 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap);
break;
case TiffEncodingMode.Gray:
- imageDataBytes = writer.WriteGray(image, this.padding);
+ imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType);
break;
default:
imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType);
@@ -378,7 +378,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private ushort GetCompressionType()
{
if (this.CompressionType == TiffEncoderCompression.Deflate &&
- this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb)
+ this.Mode == TiffEncodingMode.Rgb)
+ {
+ return (ushort)TiffCompression.Deflate;
+ }
+
+ if (this.CompressionType == TiffEncoderCompression.Deflate &&
+ this.Mode == TiffEncodingMode.Gray)
{
return (ushort)TiffCompression.Deflate;
}
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
index c95378356..028d53ab8 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
@@ -272,12 +272,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The pixel data.
/// The image to write to the stream.
/// The padding bytes for each row.
+ /// The compression to use.
/// The number of bytes written.
- public int WriteGray(Image image, int padding)
+ public int WriteGray(Image image, int padding, TiffEncoderCompression compression)
where TPixel : unmanaged, IPixel
{
using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding);
Span rowSpan = row.GetSpan();
+
+ if (compression == TiffEncoderCompression.Deflate)
+ {
+ return this.WriteGrayDeflateCompressed(image, rowSpan);
+ }
+
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
@@ -290,6 +297,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
+ ///
+ /// Writes the image data as 8 bit gray with deflate compression to the stream.
+ ///
+ /// The image to write to the stream.
+ /// A span of a pixel row.
+ /// The number of bytes written.
+ private int WriteGrayDeflateCompressed(Image image, Span rowSpan)
+ where TPixel : unmanaged, IPixel
+ {
+ int bytesWritten = 0;
+ using var memoryStream = new MemoryStream();
+
+ // TODO: move zlib compression from png to a common place?
+ using var deflateStream =
+ new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable
+
+ for (int y = 0; y < image.Height; y++)
+ {
+ Span pixelRow = image.GetPixelRowSpan(y);
+ PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
+ deflateStream.Write(rowSpan);
+ }
+
+ deflateStream.Flush();
+
+ byte[] buffer = memoryStream.ToArray();
+ this.output.Write(buffer);
+ bytesWritten += buffer.Length;
+ return bytesWritten;
+ }
+
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding);
///
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index 9d24132a4..458acd34c 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
+
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
@@ -59,6 +60,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeGray_Works(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray);
+ [Theory]
+ [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
+ public void TiffEncoder_EncodeGray_WithDeflateCompression_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate);
+
+ // 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_Works(TestImageProvider provider)