diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
index 881b3cc4ee..a83e0606c6 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
@@ -19,6 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
public TiffBitsPerPixel? BitsPerPixel { get; set; }
+ ///
+ /// Gets or sets a value indicating which compression to use.
+ ///
+ public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None;
+
///
public void Encode(Image image, Stream stream)
where TPixel : unmanaged, IPixel
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
new file mode 100644
index 0000000000..334262dbfc
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tiff
+{
+ ///
+ /// Indicates which tiff compression is used.
+ ///
+ public enum TiffEncoderCompression
+ {
+ ///
+ /// No compression is used.
+ ///
+ None,
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index d81acf6c6a..ffccce5204 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -47,21 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator)
{
this.memoryAllocator = memoryAllocator;
-
- if (options.BitsPerPixel == TiffBitsPerPixel.Pixel8)
- {
- this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
- }
- else
- {
- this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
- }
}
///
/// Gets the photometric interpretation implementation to use when encoding the image.
///
- private TiffPhotometricInterpretation PhotometricInterpretation { get; }
+ private TiffPhotometricInterpretation PhotometricInterpretation { get; set; }
///
/// Gets or sets the compression implementation to use when encoding the image.
@@ -85,6 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
ImageMetadata metadata = image.Metadata;
TiffMetadata tiffMetadata = metadata.GetTiffMetadata();
this.bitsPerPixel ??= tiffMetadata.BitsPerPixel;
+ this.PhotometricInterpretation = this.bitsPerPixel == TiffBitsPerPixel.Pixel8 ? TiffPhotometricInterpretation.BlackIsZero : TiffPhotometricInterpretation.Rgb;
short bpp = (short)this.bitsPerPixel;
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
@@ -132,15 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// Write the image bytes to the steam.
var imageDataStart = (uint)writer.Position;
- int imageDataBytes;
- if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb)
- {
- imageDataBytes = writer.WriteRgbImageData(image, this.padding);
- }
- else
- {
- imageDataBytes = writer.WriteGrayImageData(image, this.padding);
- }
+ int imageDataBytes = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? writer.WriteRgbImageData(image, this.padding) : writer.WriteGrayImageData(image, this.padding);
// Write info's about the image to the stream.
this.AddImageFormat(image, ifdEntries, imageDataStart, imageDataBytes);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
index a26f0b1172..3a0ceae74a 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
@@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using (Stream stream = CreateCompressedStream(data))
{
- byte[] buffer = new byte[data.Length];
+ var buffer = new byte[data.Length];
new LzwTiffCompression(Configuration.Default.MemoryAllocator).Decompress(stream, (int)stream.Length, buffer);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs
index 28fbce69fd..c1cde94666 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs
@@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void Decompress_ReadsData(byte[] inputData, int byteCount, byte[] expectedResult)
{
Stream stream = new MemoryStream(inputData);
- byte[] buffer = new byte[expectedResult.Length];
+ var buffer = new byte[expectedResult.Length];
new NoneTiffCompression(null).Decompress(stream, byteCount, buffer);
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
deleted file mode 100644
index af82b4026f..0000000000
--- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.Formats.Tiff;
-using SixLabors.ImageSharp.PixelFormats;
-using Xunit;
-
-namespace SixLabors.ImageSharp.Tests.Formats.Tiff
-{
- [Trait("Category", "Tiff.BlackBox.Encoder")]
- [Trait("Category", "Tiff")]
- public class ImageExtensionsTest
- {
- [Theory]
- [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
- public void ThrowsSavingNotImplemented(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
- {
- Assert.Throws(() =>
- {
- string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
- string file = Path.Combine(dir, "SaveAsTiff_Path.tiff");
- using var image = provider.GetImage(new TiffDecoder());
- image.SaveAsTiff(file);
- });
- }
-
- [Fact(Skip = "Saving not implemented")]
- public void SaveAsTiff_Path()
- {
- string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
- string file = Path.Combine(dir, "SaveAsTiff_Path.tiff");
-
- using (var image = new Image(10, 10))
- {
- image.SaveAsTiff(file);
- }
-
- using (Image.Load(file, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public async Task SaveAsTiffAsync_Path()
- {
- string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
- string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff");
-
- using (var image = new Image(10, 10))
- {
- await image.SaveAsTiffAsync(file);
- }
-
- using (Image.Load(file, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public void SaveAsTiff_Path_Encoder()
- {
- string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
- string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff");
-
- using (var image = new Image(10, 10))
- {
- image.SaveAsTiff(file, new TiffEncoder());
- }
-
- using (Image.Load(file, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public async Task SaveAsTiffAsync_Path_Encoder()
- {
- string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
- string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff");
-
- using (var image = new Image(10, 10))
- {
- await image.SaveAsTiffAsync(file, new TiffEncoder());
- }
-
- using (Image.Load(file, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public void SaveAsTiff_Stream()
- {
- using var memoryStream = new MemoryStream();
-
- using (var image = new Image(10, 10))
- {
- image.SaveAsTiff(memoryStream);
- }
-
- memoryStream.Position = 0;
-
- using (Image.Load(memoryStream, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public async Task SaveAsTiffAsync_StreamAsync()
- {
- using var memoryStream = new MemoryStream();
-
- using (var image = new Image(10, 10))
- {
- await image.SaveAsTiffAsync(memoryStream);
- }
-
- memoryStream.Position = 0;
-
- using (Image.Load(memoryStream, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public void SaveAsTiff_Stream_Encoder()
- {
- using var memoryStream = new MemoryStream();
-
- using (var image = new Image(10, 10))
- {
- image.SaveAsTiff(memoryStream, new TiffEncoder());
- }
-
- memoryStream.Position = 0;
-
- using (Image.Load(memoryStream, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
-
- [Fact(Skip = "Saving not implemented")]
- public async Task SaveAsTiffAsync_Stream_Encoder()
- {
- using var memoryStream = new MemoryStream();
-
- using (var image = new Image(10, 10))
- {
- await image.SaveAsTiffAsync(memoryStream, new TiffEncoder());
- }
-
- memoryStream.Position = 0;
-
- using (Image.Load(memoryStream, out IImageFormat mime))
- {
- Assert.Equal("image/tiff", mime.DefaultMimeType);
- }
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
index 91166bf2d6..e279ea5622 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
@@ -13,12 +13,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
private static readonly MemoryAllocator MemoryAllocator = new ArrayPoolMemoryAllocator();
private static readonly Configuration Configuration = Configuration.Default;
+ private static readonly ITiffEncoderOptions Options = new TiffEncoder();
[Fact]
public void WriteHeader_WritesValidHeader()
{
- var stream = new MemoryStream();
- var encoder = new TiffEncoderCore(null, MemoryAllocator);
+ using var stream = new MemoryStream();
+ var encoder = new TiffEncoderCore(Options, MemoryAllocator);
using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration))
{
@@ -31,8 +32,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Fact]
public void WriteHeader_ReturnsFirstIfdMarker()
{
- var stream = new MemoryStream();
- var encoder = new TiffEncoderCore(null, MemoryAllocator);
+ using var stream = new MemoryStream();
+ var encoder = new TiffEncoderCore(Options, MemoryAllocator);
using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration))
{
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
new file mode 100644
index 0000000000..4d9ea661d3
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.IO;
+
+using SixLabors.ImageSharp.Formats.Tiff;
+using SixLabors.ImageSharp.PixelFormats;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Tiff
+{
+ public class TiffEncoderTests
+ {
+ public static readonly TheoryData TiffBitsPerPixelFiles =
+ new TheoryData
+ {
+ { TestImages.Tiff.GrayscaleUncompressed, TiffBitsPerPixel.Pixel8 },
+ { TestImages.Tiff.RgbUncompressed, TiffBitsPerPixel.Pixel24 },
+ };
+
+ [Theory]
+ [MemberData(nameof(TiffBitsPerPixelFiles))]
+ public void TiffEncoder_PreserveBitsPerPixel(string imagePath, TiffBitsPerPixel expectedBitsPerPixel)
+ {
+ // arrange
+ var tiffEncoder = new TiffEncoder();
+ var testFile = TestFile.Create(imagePath);
+ using Image input = testFile.CreateRgba32Image();
+ using var memStream = new MemoryStream();
+
+ // act
+ input.Save(memStream, tiffEncoder);
+
+ // assert
+ memStream.Position = 0;
+ using var output = Image.Load(memStream);
+ TiffMetadata meta = output.Metadata.GetTiffMetadata();
+ Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel);
+ }
+
+ [Theory]
+ [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
+ public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel);
+
+ [Theory]
+ [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
+ public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel);
+
+ private static void TestTiffEncoderCore(
+ TestImageProvider provider,
+ TiffBitsPerPixel bitsPerPixel,
+ TiffEncoderCompression compression = TiffEncoderCompression.None,
+ bool useExactComparer = true,
+ float compareTolerance = 0.01f)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage();
+ var encoder = new TiffEncoder { BitsPerPixel = bitsPerPixel, Compression = compression };
+
+ using var memStream = new MemoryStream();
+ image.Save(memStream, encoder);
+ memStream.Position = 0;
+ using var encodedImage = (Image)Image.Load(memStream);
+ TiffTestUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs
new file mode 100644
index 0000000000..edf3483302
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.IO;
+using ImageMagick;
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Tiff
+{
+ public class TiffTestUtils
+ {
+ public static void CompareWithReferenceDecoder(
+ TestImageProvider provider,
+ Image image,
+ bool useExactComparer = true,
+ float compareTolerance = 0.01f)
+ where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel
+ {
+ string path = TestImageProvider.GetFilePathOrNull(provider);
+ if (path == null)
+ {
+ throw new InvalidOperationException("CompareToOriginal() works only with file providers!");
+ }
+
+ var testFile = TestFile.Create(path);
+ Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath));
+ if (useExactComparer)
+ {
+ ImageComparer.Exact.VerifySimilarity(magickImage, image);
+ }
+ else
+ {
+ ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image);
+ }
+ }
+
+ public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo)
+ where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel
+ {
+ using var magickImage = new MagickImage(fileInfo);
+ magickImage.AutoOrient();
+ var result = new Image(configuration, magickImage.Width, magickImage.Height);
+
+ Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels));
+
+ using IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe();
+ byte[] data = pixels.ToByteArray(PixelMapping.RGBA);
+
+ PixelOperations.Instance.FromRgba32Bytes(
+ configuration,
+ data,
+ resultPixels,
+ resultPixels.Length);
+
+ return result;
+ }
+ }
+}