diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
index 334262dbfc..536cd2c2d3 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
@@ -12,5 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// No compression is used.
///
None,
+
+ ///
+ /// Use zlib compression.
+ ///
+ Deflate
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index c493d34a41..99b299d043 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -150,16 +150,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// Write the image bytes to the steam.
var imageDataStart = (uint)writer.Position;
int imageDataBytes;
- switch (this.PhotometricInterpretation)
+ switch (this.Mode)
{
- case TiffPhotometricInterpretation.PaletteColor:
+ case TiffEncodingMode.ColorPalette:
imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap);
break;
- case TiffPhotometricInterpretation.BlackIsZero:
+ case TiffEncodingMode.Gray:
imageDataBytes = writer.WriteGrayImageData(image, this.padding);
break;
default:
- imageDataBytes = writer.WriteRgbImageData(image, this.padding);
+ imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType);
break;
}
@@ -260,10 +260,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Value = bitsPerSampleValue
};
+ ushort compressionType = this.GetCompressionType();
var compression = new ExifShort(ExifTagValue.Compression)
{
- // TODO: for the start, no compression is used.
- Value = (ushort)TiffCompression.None
+ Value = compressionType
};
var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation)
@@ -374,5 +374,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return new ushort[] { 8, 8, 8 };
}
}
+
+ private ushort GetCompressionType()
+ {
+ if (this.CompressionType == TiffEncoderCompression.Deflate &&
+ this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb)
+ {
+ 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 e99682bc08..8ef57f4b2a 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
@@ -6,6 +6,9 @@ using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
+
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
@@ -133,13 +136,37 @@ 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 WriteRgbImageData(Image image, int padding)
+ public int WriteRgbImageData(Image image, int padding, TiffEncoderCompression compression)
where TPixel : unmanaged, IPixel
{
using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding);
Span rowSpan = row.GetSpan();
int bytesWritten = 0;
+ if (compression == TiffEncoderCompression.Deflate)
+ {
+ 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.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
+ deflateStream.Write(rowSpan);
+ }
+
+ deflateStream.Flush();
+
+ byte[] buffer = memoryStream.ToArray();
+ this.output.Write(buffer);
+ bytesWritten += buffer.Length;
+ return bytesWritten;
+ }
+
+ // No compression.
for (int y = 0; y < image.Height; y++)
{
Span pixelRow = image.GetPixelRowSpan(y);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index 27ca717e66..cef8cecc7e 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -3,10 +3,11 @@
using System.IO;
+using SixLabors.ImageSharp.Formats;
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
@@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Trait("Category", "Tiff")]
public class TiffEncoderTests
{
- private static TiffDecoder referenceDecoder = new TiffDecoder();
+ private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
public static readonly TheoryData TiffBitsPerPixelFiles =
new TheoryData
@@ -45,18 +46,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
- public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.Rgb)
- where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
+ public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb);
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
- public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, TiffEncodingMode mode = TiffEncodingMode.Gray)
- where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
+ public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate);
+
+ [Theory]
+ [WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
+ public void TiffEncoder_EncodeGray_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray);
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
- public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.ColorPalette)
- where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
+ public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette);
private static void TestTiffEncoderCore(
TestImageProvider provider,
@@ -71,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var encoder = new TiffEncoder { Mode = mode, Compression = compression };
// Does DebugSave & load reference CompareToReferenceInput():
- image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: referenceDecoder);
+ image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder);
}
}
}