diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
index 5b849e1314..97f3d46b06 100644
--- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
@@ -10,20 +10,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
internal interface ITiffEncoderOptions
{
- ///
- /// Gets the number of bits per pixel.
- ///
- TiffBitsPerPixel? BitsPerPixel { get; }
-
///
/// Gets the compression type to use.
///
TiffEncoderCompression Compression { get; }
///
- /// Gets a value indicating whether to use a color palette.
+ /// Gets the encoding mode to use. RGB, RGB with color palette or gray.
///
- bool UseColorPalette { get; }
+ TiffEncodingMode Mode { get; }
///
/// Gets the quantizer for creating a color palette image.
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
index 409d16a68f..3ab17b6c37 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
@@ -15,20 +15,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
public class TiffEncoder : IImageEncoder, ITiffEncoderOptions
{
- ///
- /// Gets or sets the number of bits per pixel. 8 bit implies a grayscale image.
- ///
- public TiffBitsPerPixel? BitsPerPixel { get; set; }
-
///
/// Gets or sets a value indicating which compression to use.
///
public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None;
///
- /// Gets or sets a value indicating whether to use a color palette.
+ /// Gets or sets the encoding mode to use. RGB, RGB with a color palette or gray.
///
- public bool UseColorPalette { get; set; }
+ public TiffEncodingMode Mode { get; set; }
///
/// Gets or sets the quantizer for color images with a palette.
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index f2aec7a61b..c493d34a41 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
this.memoryAllocator = memoryAllocator;
this.CompressionType = options.Compression;
- this.UseColorMap = options.UseColorPalette;
+ this.Mode = options.Mode;
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
}
@@ -70,9 +70,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private TiffEncoderCompression CompressionType { get; }
///
- /// Gets a value indicating whether to use a colormap.
+ /// Gets or sets the encoding mode to use. RGB, RGB with color palette or gray.
///
- private bool UseColorMap { get; }
+ private TiffEncodingMode Mode { get; set; }
///
/// Encodes the image to the specified stream from the .
@@ -91,6 +91,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
ImageMetadata metadata = image.Metadata;
TiffMetadata tiffMetadata = metadata.GetTiffMetadata();
this.bitsPerPixel ??= tiffMetadata.BitsPerPixel;
+ if (this.Mode == TiffEncodingMode.Default)
+ {
+ // Preserve input bits per pixel, if no mode was specified.
+ if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8)
+ {
+ this.Mode = TiffEncodingMode.Gray;
+ }
+ }
+
this.SetPhotometricInterpretation();
short bpp = (short)this.bitsPerPixel;
@@ -141,17 +150,17 @@ 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 if (this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor)
- {
- imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap);
- }
- else
+ switch (this.PhotometricInterpretation)
{
- imageDataBytes = writer.WriteGrayImageData(image, this.padding);
+ case TiffPhotometricInterpretation.PaletteColor:
+ imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap);
+ break;
+ case TiffPhotometricInterpretation.BlackIsZero:
+ imageDataBytes = writer.WriteGrayImageData(image, this.padding);
+ break;
+ default:
+ imageDataBytes = writer.WriteRgbImageData(image, this.padding);
+ break;
}
// Write info's about the image to the stream.
@@ -270,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel)
{
- Value = 3
+ Value = this.GetSamplesPerPixel()
};
var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip)
@@ -323,19 +332,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private void SetPhotometricInterpretation()
{
- if (this.UseColorMap)
+ switch (this.Mode)
{
- this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor;
- return;
+ case TiffEncodingMode.ColorPalette:
+ this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor;
+ break;
+ case TiffEncodingMode.Gray:
+ this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
+ break;
+ default:
+ this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
+ break;
}
+ }
- if (this.bitsPerPixel == TiffBitsPerPixel.Pixel8)
- {
- this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
- }
- else
+ private uint GetSamplesPerPixel()
+ {
+ switch (this.PhotometricInterpretation)
{
- this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
+ case TiffPhotometricInterpretation.PaletteColor:
+ case TiffPhotometricInterpretation.Rgb:
+ return 3;
+ case TiffPhotometricInterpretation.BlackIsZero:
+ return 1;
+ default:
+ return 3;
}
}
@@ -344,6 +365,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (this.PhotometricInterpretation)
{
case TiffPhotometricInterpretation.PaletteColor:
+ return new ushort[] { 8 };
case TiffPhotometricInterpretation.Rgb:
return new ushort[] { 8, 8, 8 };
case TiffPhotometricInterpretation.BlackIsZero:
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs
new file mode 100644
index 0000000000..195353ec4e
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tiff
+{
+ ///
+ /// Enum for the different tiff encoding options.
+ ///
+ public enum TiffEncodingMode
+ {
+ ///
+ /// No mode specified. Will preserve the bits per pixels of the input image.
+ ///
+ Default = 0,
+
+ ///
+ /// The image will be encoded as RGB, 8 bit per channel.
+ ///
+ Rgb = 1,
+
+ ///
+ /// The image will be encoded as RGB with a color palette.
+ ///
+ ColorPalette = 2,
+
+ ///
+ /// The image will be encoded as 8 bit gray.
+ ///
+ Gray = 3,
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
index 16c9b87e32..e99682bc08 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
@@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The pixel data.
/// The image to write to the stream.
/// The padding bytes for each row.
- /// The number of bytes written
+ /// The number of bytes written.
public int WriteRgbImageData(Image image, int padding)
where TPixel : unmanaged, IPixel
{
@@ -151,6 +151,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
+ ///
+ /// Writes the image data as indices into a color map to the stream.
+ ///
+ /// The pixel data.
+ /// The image to write to the stream.
+ /// The quantizer to use.
+ /// The padding bytes for each row.
+ /// The color map.
+ /// The number of bytes written.
public int WritePalettedRgbImageData(Image image, IQuantizer quantizer, int padding, out IExifValue colorMap)
where TPixel : unmanaged, IPixel
{
@@ -214,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The pixel data.
/// The image to write to the stream.
/// The padding bytes for each row.
- /// The number of bytes written
+ /// The number of bytes written.
public int WriteGrayImageData(Image image, int padding)
where TPixel : unmanaged, IPixel
{
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index 16a2ab0126..27ca717e66 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -5,13 +5,17 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
+ [Trait("Category", "Tiff")]
public class TiffEncoderTests
{
+ private static TiffDecoder referenceDecoder = new TiffDecoder();
+
public static readonly TheoryData TiffBitsPerPixelFiles =
new TheoryData
{
@@ -41,36 +45,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[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);
+ public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.Rgb)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
[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);
+ public void TiffEncoder_EncodeGray_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, TiffEncodingMode mode = TiffEncodingMode.Gray)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
[Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
- public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, bool useColorPalette = true)
- where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, useColorPalette);
+ public void TiffEncoder_EncodeColorPalette_Works(TestImageProvider provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.ColorPalette)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, bitsPerPixel, mode);
private static void TestTiffEncoderCore(
TestImageProvider provider,
TiffBitsPerPixel bitsPerPixel,
- bool useColorPalette = false,
+ TiffEncodingMode mode,
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 };
+ var encoder = new TiffEncoder { Mode = mode, 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);
+ // Does DebugSave & load reference CompareToReferenceInput():
+ image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: referenceDecoder);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs
deleted file mode 100644
index edf3483302..0000000000
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffTestUtils.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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;
- }
- }
-}