Browse Source

Change TiffEncoder to use TiffPhotometricInterpretation instead of EncodingMode

pull/1553/head
Brian Popow 5 years ago
parent
commit
d22692ee8f
  1. 4
      src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs
  2. 20
      src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs
  3. 12
      src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
  4. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  5. 8
      src/ImageSharp/Formats/Tiff/TiffEncoder.cs
  6. 170
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  7. 29
      src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
  8. 36
      src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs
  9. 3
      src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs
  10. 15
      src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs
  11. 17
      src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
  12. 14
      tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs
  13. 218
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  14. 4
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

4
src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs

@ -344,8 +344,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
{ {
while (codeLength > 0) while (codeLength > 0)
{ {
var bitNumber = (int)codeLength; int bitNumber = (int)codeLength;
var bit = (code & (1 << (bitNumber - 1))) != 0; bool bit = (code & (1 << (bitNumber - 1))) != 0;
if (bit) if (bit)
{ {
BitWriterUtils.WriteBit(compressedData, this.bytePosition, this.bitPosition); BitWriterUtils.WriteBit(compressedData, this.bytePosition, this.bitPosition);

20
src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs

@ -10,6 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
{ {
/// <summary> /// <summary>
/// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black.
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
WhiteIsZero = 0, WhiteIsZero = 0,
@ -29,42 +31,58 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
PaletteColor = 3, PaletteColor = 3,
/// <summary> /// <summary>
/// A transparency mask /// A transparency mask.
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
TransparencyMask = 4, TransparencyMask = 4,
/// <summary> /// <summary>
/// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification). /// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
Separated = 5, Separated = 5,
/// <summary> /// <summary>
/// YCbCr (see Section 21 of the TIFF 6.0 specification). /// YCbCr (see Section 21 of the TIFF 6.0 specification).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
YCbCr = 6, YCbCr = 6,
/// <summary> /// <summary>
/// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification). /// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
CieLab = 8, CieLab = 8,
/// <summary> /// <summary>
/// ICC L*a*b* (see TIFF Specification, supplement 1). /// ICC L*a*b* (see TIFF Specification, supplement 1).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
IccLab = 9, IccLab = 9,
/// <summary> /// <summary>
/// ITU L*a*b* (see RFC2301). /// ITU L*a*b* (see RFC2301).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
ItuLab = 10, ItuLab = 10,
/// <summary> /// <summary>
/// Color Filter Array (see the DNG specification). /// Color Filter Array (see the DNG specification).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
ColorFilterArray = 32803, ColorFilterArray = 32803,
/// <summary> /// <summary>
/// Linear Raw (see the DNG specification). /// Linear Raw (see the DNG specification).
///
/// Not supported by the TiffEncoder.
/// </summary> /// </summary>
LinearRaw = 34892 LinearRaw = 34892
} }

12
src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs

@ -20,24 +20,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <summary> /// <summary>
/// Gets the compression type to use. /// Gets the compression type to use.
/// </summary> /// </summary>
TiffCompression Compression { get; } TiffCompression? Compression { get; }
/// <summary> /// <summary>
/// Gets the compression level 1-9 for the deflate compression mode. /// Gets the compression level 1-9 for the deflate compression mode.
/// <remarks>Defaults to <see cref="DeflateCompressionLevel.DefaultCompression"/>.</remarks> /// <remarks>Defaults to <see cref="DeflateCompressionLevel.DefaultCompression"/>.</remarks>
/// </summary> /// </summary>
DeflateCompressionLevel CompressionLevel { get; } DeflateCompressionLevel? CompressionLevel { get; }
/// <summary> /// <summary>
/// Gets the encoding mode to use. Possible options are RGB, RGB with a color palette, gray or BiColor. /// Gets the PhotometricInterpretation to use. Possible options are RGB, RGB with a color palette, gray or BiColor.
/// If no mode is specified in the options, RGB will be used. /// If no PhotometricInterpretation is specified or it is unsupported by the encoder, RGB will be used.
/// </summary> /// </summary>
TiffEncodingMode Mode { get; } TiffPhotometricInterpretation? PhotometricInterpretation { get; }
/// <summary> /// <summary>
/// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression. /// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression.
/// </summary> /// </summary>
TiffPredictor HorizontalPredictor { get; } TiffPredictor? HorizontalPredictor { get; }
/// <summary> /// <summary>
/// Gets the quantizer for creating a color palette image. /// Gets the quantizer for creating a color palette image.

2
src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration; options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
options.Predictor = predictor; options.Predictor = predictor;
options.PhotometricInterpretation = exifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ? options.PhotometricInterpretation = exifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ?
(TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero; (TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.BlackIsZero;
options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel); options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel);

8
src/ImageSharp/Formats/Tiff/TiffEncoder.cs

@ -22,16 +22,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffBitsPerPixel? BitsPerPixel { get; set; } public TiffBitsPerPixel? BitsPerPixel { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public TiffCompression Compression { get; set; } = TiffCompression.None; public TiffCompression? Compression { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; public DeflateCompressionLevel? CompressionLevel { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public TiffEncodingMode Mode { get; set; } public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public TiffPredictor HorizontalPredictor { get; set; } public TiffPredictor? HorizontalPredictor { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public IQuantizer Quantizer { get; set; } public IQuantizer Quantizer { get; set; }

170
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Formats.Tiff.Writers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
@ -61,34 +62,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator)
{ {
this.memoryAllocator = memoryAllocator; this.memoryAllocator = memoryAllocator;
this.Mode = options.Mode; this.PhotometricInterpretation = options.PhotometricInterpretation;
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
this.BitsPerPixel = options.BitsPerPixel; this.BitsPerPixel = options.BitsPerPixel;
this.HorizontalPredictor = options.HorizontalPredictor; this.HorizontalPredictor = options.HorizontalPredictor;
this.CompressionType = options.Compression != TiffCompression.Invalid ? options.Compression : TiffCompression.None; this.CompressionType = options.Compression;
this.compressionLevel = options.CompressionLevel; this.compressionLevel = options.CompressionLevel ?? DeflateCompressionLevel.DefaultCompression;
} }
/// <summary> /// <summary>
/// Gets the photometric interpretation implementation to use when encoding the image. /// Gets the photometric interpretation implementation to use when encoding the image.
/// </summary> /// </summary>
internal TiffPhotometricInterpretation PhotometricInterpretation { get; private set; } internal TiffPhotometricInterpretation? PhotometricInterpretation { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the compression implementation to use when encoding the image. /// Gets or sets the compression implementation to use when encoding the image.
/// </summary> /// </summary>
internal TiffCompression CompressionType { get; set; } internal TiffCompression? CompressionType { get; set; }
/// <summary> /// <summary>
/// Gets the encoding mode to use. RGB, RGB with color palette or gray. /// Gets or sets a value indicating which horizontal predictor to use. This can improve the compression ratio with deflate compression.
/// If no mode is specified in the options, RGB will be used.
/// </summary> /// </summary>
internal TiffEncodingMode Mode { get; private set; } internal TiffPredictor? HorizontalPredictor { get; set; }
/// <summary>
/// Gets a value indicating which horizontal predictor to use. This can improve the compression ratio with deflate compression.
/// </summary>
internal TiffPredictor HorizontalPredictor { get; }
/// <summary> /// <summary>
/// Gets the bits per pixel. /// Gets the bits per pixel.
@ -111,18 +106,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.configuration = image.GetConfiguration(); this.configuration = image.GetConfiguration();
TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image); TiffPhotometricInterpretation rootFramePhotometricInterpretation = GetRootFramePhotometricInterpretation(image);
TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette TiffPhotometricInterpretation photometricInterpretation = this.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor
? TiffPhotometricInterpretation.PaletteColor ? TiffPhotometricInterpretation.PaletteColor
: rootFramePhotometricInterpretation; : rootFramePhotometricInterpretation;
TiffBitsPerPixel? rootFrameBitsPerPixel = image.Frames.RootFrame.Metadata.GetTiffMetadata().BitsPerPixel; ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata;
ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile;
TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameMetaData.GetTiffMetadata().BitsPerPixel;
// If the user has not chosen a predictor or compression, set the values from the decoded image, if present.
if (!this.HorizontalPredictor.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Predictor) != null)
{
this.HorizontalPredictor = (TiffPredictor)rootFrameExifProfile?.GetValue(ExifTag.Predictor).Value;
}
if (!this.CompressionType.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Compression) != null)
{
this.CompressionType = (TiffCompression)rootFrameExifProfile?.GetValue(ExifTag.Compression).Value;
}
// TODO: This isn't correct. this.SetBitsPerPixel(rootFrameBitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation);
// We're overwriting explicit BPP based upon the Mode. It should be the other way around. this.SetPhotometricInterpretation(photometricInterpretation);
// BPP should also be nullable and based upon the current TPixel if not set.
this.SetBitsPerPixel(rootFrameBitsPerPixel, photometricInterpretation);
this.SetMode(photometricInterpretation);
this.SetPhotometricInterpretation();
using (var writer = new TiffStreamWriter(stream)) using (var writer = new TiffStreamWriter(stream))
{ {
@ -159,20 +163,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{ {
var entriesCollector = new TiffEncoderEntriesCollector(); var entriesCollector = new TiffEncoderEntriesCollector();
// Write the image bytes to the steam.
uint imageDataStart = (uint)writer.Position;
using TiffBaseCompressor compressor = TiffCompressorFactory.Create( using TiffBaseCompressor compressor = TiffCompressorFactory.Create(
this.CompressionType, this.CompressionType ?? TiffCompression.None,
writer.BaseStream, writer.BaseStream,
this.memoryAllocator, this.memoryAllocator,
image.Width, image.Width,
(int)this.BitsPerPixel, (int)this.BitsPerPixel,
this.compressionLevel, this.compressionLevel,
this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None); this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None);
using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create( using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create(
this.Mode, this.PhotometricInterpretation,
image.Frames.RootFrame, image.Frames.RootFrame,
this.quantizer, this.quantizer,
this.memoryAllocator, this.memoryAllocator,
@ -227,8 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{ {
if (entries.Count == 0) if (entries.Count == 0)
{ {
// TODO: Perf. Throwhelper TiffThrowHelper.ThrowArgumentException("There must be at least one entry per IFD.");
throw new ArgumentException("There must be at least one entry per IFD.", nameof(entries));
} }
uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12)); uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12));
@ -277,52 +277,73 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return nextIfdMarker; return nextIfdMarker;
} }
private void SetMode(TiffPhotometricInterpretation photometricInterpretation) private void SetPhotometricInterpretation(TiffPhotometricInterpretation? photometricInterpretation)
{ {
// Make sure, that the fax compressions are only used together with the BiColor mode. // Make sure, that the fax compressions are only used together with the WhiteIsZero.
if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D)
{ {
// Default means the user has not specified a preferred encoding mode. // The user has not specified a preferred photometric interpretation.
if (this.Mode == TiffEncodingMode.Default) if (this.PhotometricInterpretation == null)
{ {
this.Mode = TiffEncodingMode.BiColor; this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
this.BitsPerPixel = TiffBitsPerPixel.Bit1; this.BitsPerPixel = TiffBitsPerPixel.Bit1;
return; return;
} }
if (this.Mode != TiffEncodingMode.BiColor) if (this.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && this.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
{ {
TiffThrowHelper.ThrowImageFormatException($"The {this.CompressionType} compression and {this.Mode} aren't compatible. Please use {this.CompressionType} only with {TiffEncodingMode.BiColor} or {TiffEncodingMode.Default} mode."); TiffThrowHelper.ThrowImageFormatException(
$"The {this.CompressionType} compression and {this.PhotometricInterpretation} aren't compatible. Please use {this.CompressionType} only with {TiffPhotometricInterpretation.BlackIsZero} or {TiffPhotometricInterpretation.WhiteIsZero}.");
} }
else
{
// The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero.
this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
}
return;
}
switch (this.PhotometricInterpretation)
{
// The currently supported values by the encoder for photometric interpretation:
case TiffPhotometricInterpretation.PaletteColor:
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
break;
default:
this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
break;
} }
// Use the bits per pixel to determine the encoding mode. // Use the bits per pixel to determine the photometric interpretation.
this.SetModeWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation); this.SetPhotometricInterpretationWithBitsPerPixel(this.BitsPerPixel, photometricInterpretation);
} }
private void SetModeWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) private void SetPhotometricInterpretationWithBitsPerPixel(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation)
{ {
switch (bitsPerPixel) switch (bitsPerPixel)
{ {
case TiffBitsPerPixel.Bit1: case TiffBitsPerPixel.Bit1:
this.Mode = TiffEncodingMode.BiColor; this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
break; break;
case TiffBitsPerPixel.Bit4: case TiffBitsPerPixel.Bit4:
this.Mode = TiffEncodingMode.ColorPalette; this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor;
break; break;
case TiffBitsPerPixel.Bit8: case TiffBitsPerPixel.Bit8:
this.Mode = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor this.PhotometricInterpretation = photometricInterpretation == TiffPhotometricInterpretation.PaletteColor
? TiffEncodingMode.ColorPalette ? TiffPhotometricInterpretation.PaletteColor
: TiffEncodingMode.Gray; : TiffPhotometricInterpretation.BlackIsZero;
break; break;
default: default:
this.Mode = TiffEncodingMode.Rgb; this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
break; break;
} }
} }
private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) private void SetBitsPerPixel(TiffBitsPerPixel? rootFrameBitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation photometricInterpretation)
{ {
this.BitsPerPixel ??= rootFrameBitsPerPixel; this.BitsPerPixel ??= rootFrameBitsPerPixel;
@ -341,56 +362,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return; return;
} }
switch (this.Mode) if (this.PhotometricInterpretation == null && inputBitsPerPixel == 8)
{ {
case TiffEncodingMode.BiColor: this.BitsPerPixel = TiffBitsPerPixel.Bit8;
this.BitsPerPixel = TiffBitsPerPixel.Bit1; return;
break;
case TiffEncodingMode.ColorPalette:
if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4)
{
this.BitsPerPixel = TiffBitsPerPixel.Bit8;
}
break;
case TiffEncodingMode.Gray:
this.BitsPerPixel = TiffBitsPerPixel.Bit8;
break;
case TiffEncodingMode.Rgb:
this.BitsPerPixel = TiffBitsPerPixel.Bit24;
break;
default:
this.Mode = TiffEncodingMode.Rgb;
this.BitsPerPixel = TiffBitsPerPixel.Bit24;
break;
} }
}
private void SetPhotometricInterpretation() switch (this.PhotometricInterpretation)
{
switch (this.Mode)
{ {
case TiffEncodingMode.ColorPalette: case TiffPhotometricInterpretation.BlackIsZero:
this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor; case TiffPhotometricInterpretation.WhiteIsZero:
break; if (this.CompressionType == TiffCompression.Ccitt1D ||
case TiffEncodingMode.BiColor: this.CompressionType == TiffCompression.CcittGroup3Fax ||
if (this.CompressionType == TiffCompression.CcittGroup3Fax || this.CompressionType == TiffCompression.Ccitt1D) this.CompressionType == TiffCompression.CcittGroup4Fax)
{ {
// The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. this.BitsPerPixel = TiffBitsPerPixel.Bit1;
this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
} }
else else
{ {
this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; this.BitsPerPixel = TiffBitsPerPixel.Bit8;
} }
break; break;
case TiffPhotometricInterpretation.PaletteColor:
if (this.BitsPerPixel != TiffBitsPerPixel.Bit8 && this.BitsPerPixel != TiffBitsPerPixel.Bit4)
{
this.BitsPerPixel = TiffBitsPerPixel.Bit8;
}
case TiffEncodingMode.Gray: break;
this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; case TiffPhotometricInterpretation.Rgb:
this.BitsPerPixel = TiffBitsPerPixel.Bit24;
break; break;
default: default:
this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
this.BitsPerPixel = TiffBitsPerPixel.Bit24;
break; break;
} }
} }
@ -400,7 +406,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile; ExifProfile exifProfile = image.Frames.RootFrame.Metadata.ExifProfile;
return exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null return exifProfile?.GetValue(ExifTag.PhotometricInterpretation) != null
? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value ? (TiffPhotometricInterpretation)exifProfile?.GetValue(ExifTag.PhotometricInterpretation).Value
: TiffPhotometricInterpretation.WhiteIsZero; : TiffPhotometricInterpretation.BlackIsZero;
} }
} }
} }

29
src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs

@ -281,7 +281,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
if (encoder.HorizontalPredictor == TiffPredictor.Horizontal) if (encoder.HorizontalPredictor == TiffPredictor.Horizontal)
{ {
if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) if (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ||
encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ||
encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
{ {
var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal };
@ -320,20 +322,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffPhotometricInterpretation.Rgb: case TiffPhotometricInterpretation.Rgb:
return TiffConstants.BitsPerSampleRgb8Bit; return TiffConstants.BitsPerSampleRgb8Bit;
case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.WhiteIsZero:
if (encoder.Mode == TiffEncodingMode.BiColor) if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1)
{ {
return TiffConstants.BitsPerSample1Bit; return TiffConstants.BitsPerSample1Bit;
} }
return TiffConstants.BitsPerSample8Bit; return TiffConstants.BitsPerSample8Bit;
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
if (encoder.Mode == TiffEncodingMode.BiColor) if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1)
{ {
return TiffConstants.BitsPerSample1Bit; return TiffConstants.BitsPerSample1Bit;
} }
return TiffConstants.BitsPerSample8Bit; return TiffConstants.BitsPerSample8Bit;
default: default:
return TiffConstants.BitsPerSampleRgb8Bit; return TiffConstants.BitsPerSampleRgb8Bit;
} }
@ -350,7 +355,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// PackBits is allowed for all modes. // PackBits is allowed for all modes.
return (ushort)TiffCompression.PackBits; return (ushort)TiffCompression.PackBits;
case TiffCompression.Lzw: case TiffCompression.Lzw:
if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) if (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ||
encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor ||
encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
{ {
return (ushort)TiffCompression.Lzw; return (ushort)TiffCompression.Lzw;
} }
@ -358,20 +365,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
break; break;
case TiffCompression.CcittGroup3Fax: case TiffCompression.CcittGroup3Fax:
if (encoder.Mode == TiffEncodingMode.BiColor) return (ushort)TiffCompression.CcittGroup3Fax;
{
return (ushort)TiffCompression.CcittGroup3Fax;
}
break;
case TiffCompression.Ccitt1D: case TiffCompression.Ccitt1D:
if (encoder.Mode == TiffEncodingMode.BiColor) return (ushort)TiffCompression.Ccitt1D;
{
return (ushort)TiffCompression.Ccitt1D;
}
break;
} }
return (ushort)TiffCompression.None; return (ushort)TiffCompression.None;

36
src/ImageSharp/Formats/Tiff/TiffEncodingMode.cs

@ -1,36 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Enum for the different tiff encoding options.
/// </summary>
public enum TiffEncodingMode
{
/// <summary>
/// No mode specified. Will preserve the bits per pixels of the input image.
/// </summary>
Default = 0,
/// <summary>
/// The image will be encoded as RGB, 8 bit per channel.
/// </summary>
Rgb = 1,
/// <summary>
/// The image will be encoded as RGB with a color palette.
/// </summary>
ColorPalette = 2,
/// <summary>
/// The image will be encoded as 8 bit gray.
/// </summary>
Gray = 3,
/// <summary>
/// The image will be written as a white and black image.
/// </summary>
BiColor = 4,
}
}

3
src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs

@ -32,5 +32,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotSupported(string message) => throw new NotSupportedException(message); public static void ThrowNotSupported(string message) => throw new NotSupportedException(message);
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowArgumentException(string message) => throw new ArgumentException(message);
} }
} }

15
src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs

@ -36,16 +36,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
/// <inheritdoc/> /// <inheritdoc/>
protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor)
{ {
if (this.pixelsAsGray == null) this.pixelsAsGray ??= this.MemoryAllocator.Allocate<byte>(height * this.Image.Width);
{
this.pixelsAsGray = this.MemoryAllocator.Allocate<byte>(height * this.Image.Width);
}
Span<byte> pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width); Span<byte> pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width);
Span<TPixel> pixels = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); Span<TPixel> pixelsBlackWhite = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixels, pixelAsGraySpan, pixels.Length); PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhite, pixelAsGraySpan, pixelsBlackWhite.Length);
if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D) if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D)
{ {
@ -54,11 +51,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
} }
else else
{ {
// Write uncompressed image.
int bytesPerStrip = this.BytesPerRow * height; int bytesPerStrip = this.BytesPerRow * height;
if (this.bitStrip == null) this.bitStrip ??= this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip);
{
this.bitStrip = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip);
}
Span<byte> rows = this.bitStrip.Slice(0, bytesPerStrip); Span<byte> rows = this.bitStrip.Slice(0, bytesPerStrip);
rows.Clear(); rows.Clear();

17
src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
internal static class TiffColorWriterFactory internal static class TiffColorWriterFactory
{ {
public static TiffBaseColorWriter<TPixel> Create<TPixel>( public static TiffBaseColorWriter<TPixel> Create<TPixel>(
TiffEncodingMode mode, TiffPhotometricInterpretation? photometricInterpretation,
ImageFrame<TPixel> image, ImageFrame<TPixel> image,
IQuantizer quantizer, IQuantizer quantizer,
MemoryAllocator memoryAllocator, MemoryAllocator memoryAllocator,
@ -19,14 +20,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int bitsPerPixel) int bitsPerPixel)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
switch (mode) switch (photometricInterpretation)
{ {
case TiffEncodingMode.ColorPalette: case TiffPhotometricInterpretation.PaletteColor:
return new TiffPaletteWriter<TPixel>(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel); return new TiffPaletteWriter<TPixel>(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffEncodingMode.Gray: case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
if (bitsPerPixel == 1)
{
return new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
}
return new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector); return new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
case TiffEncodingMode.BiColor:
return new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
default: default:
return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector); return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
} }

14
tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs

@ -61,8 +61,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public void SystemDrawing() public void SystemDrawing()
{ {
ImageCodecInfo codec = FindCodecForType("image/tiff"); ImageCodecInfo codec = FindCodecForType("image/tiff");
using var parameters = new EncoderParameters(1); using var parameters = new EncoderParameters(1)
parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)); {
Param = {[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression))}
};
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
this.drawing.Save(memoryStream, codec, parameters); this.drawing.Save(memoryStream, codec, parameters);
@ -71,15 +73,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
[Benchmark(Description = "ImageSharp Tiff")] [Benchmark(Description = "ImageSharp Tiff")]
public void TiffCore() public void TiffCore()
{ {
TiffEncodingMode mode = TiffEncodingMode.Default; TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb;
// workaround for 1-bit bug // Workaround for 1-bit bug
if (this.Compression == TiffCompression.CcittGroup3Fax || this.Compression == TiffCompression.Ccitt1D) if (this.Compression == TiffCompression.CcittGroup3Fax || this.Compression == TiffCompression.Ccitt1D)
{ {
mode = TiffEncodingMode.BiColor; photometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
} }
var encoder = new TiffEncoder() { Compression = this.Compression, Mode = mode }; var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation };
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
this.core.SaveAsTiff(memoryStream, encoder); this.core.SaveAsTiff(memoryStream, encoder);
} }

218
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

@ -31,15 +31,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
} }
[Theory] [Theory]
[InlineData(TiffEncodingMode.Default, TiffBitsPerPixel.Bit24)] [InlineData(null, TiffBitsPerPixel.Bit24)]
[InlineData(TiffEncodingMode.Rgb, TiffBitsPerPixel.Bit24)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffBitsPerPixel.Bit24)]
[InlineData(TiffEncodingMode.ColorPalette, TiffBitsPerPixel.Bit8)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffBitsPerPixel.Bit8)]
[InlineData(TiffEncodingMode.Gray, TiffBitsPerPixel.Bit8)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffBitsPerPixel.Bit8)]
[InlineData(TiffEncodingMode.BiColor, TiffBitsPerPixel.Bit1)] public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffBitsPerPixel expectedBitsPerPixel)
public void EncoderOptions_SetEncodingMode_Works(TiffEncodingMode mode, TiffBitsPerPixel expectedBitsPerPixel)
{ {
// arrange // arrange
var tiffEncoder = new TiffEncoder { Mode = mode }; var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation };
using Image input = new Image<Rgb24>(10, 10); using Image input = new Image<Rgb24>(10, 10);
using var memStream = new MemoryStream(); using var memStream = new MemoryStream();
@ -81,30 +80,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
} }
[Theory] [Theory]
[InlineData(TiffEncodingMode.Default, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.Gray, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.BiColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit1, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)]
[InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(null, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.Default, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.ColorPalette, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)]
[InlineData(TiffEncodingMode.Gray, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] [InlineData(null, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.BiColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit1, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.Gray, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)]
[InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)]
[InlineData(TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)]
[InlineData(TiffEncodingMode.BiColor, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)]
[InlineData(TiffEncodingMode.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] public void EncoderOptions_SetPhotometricInterpretationAndCompression_Works(TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression)
public void EncoderOptions_SetEncodingModeAndCompression_Works(TiffEncodingMode mode, TiffCompression compression, TiffBitsPerPixel expectedBitsPerPixel, TiffCompression expectedCompression)
{ {
// arrange // arrange
var tiffEncoder = new TiffEncoder { Mode = mode, Compression = compression }; var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation, Compression = compression };
using Image input = new Image<Rgb24>(10, 10); using Image input = new Image<Rgb24>(10, 10);
using var memStream = new MemoryStream(); using var memStream = new MemoryStream();
@ -115,8 +113,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
memStream.Position = 0; memStream.Position = 0;
using var output = Image.Load<Rgba32>(Configuration, memStream); using var output = Image.Load<Rgba32>(Configuration, memStream);
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile; ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
var frameMetaData = TiffFrameMetadata.Parse(exifProfile); TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel);
Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
} }
@ -146,6 +144,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel); Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
} }
[Fact]
public void TiffEncoder_PreservesBitsPerPixel_WhenInputIsL8()
{
// arrange
var tiffEncoder = new TiffEncoder();
using Image input = new Image<L8>(10, 10);
using var memStream = new MemoryStream();
var expectedBitsPerPixel = TiffBitsPerPixel.Bit8;
// act
input.Save(memStream, tiffEncoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(Configuration, memStream);
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
var frameMetaData = TiffFrameMetadata.Parse(exifProfile);
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
}
[Theory]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.None)]
[WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffCompression.Lzw)]
[WithFile(RgbDeflate, PixelTypes.Rgba32, TiffCompression.Deflate)]
[WithFile(RgbPackbits, PixelTypes.Rgba32, TiffCompression.PackBits)]
public void TiffEncoder_PreservesCompression<TPixel>(TestImageProvider<TPixel> provider, TiffCompression expectedCompression)
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
var tiffEncoder = new TiffEncoder();
using Image<TPixel> input = provider.GetImage();
using var memStream = new MemoryStream();
// act
input.Save(memStream, tiffEncoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(Configuration, memStream);
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
}
[Theory]
[WithFile(RgbLzwNoPredictor, PixelTypes.Rgba32, TiffPredictor.None)]
[WithFile(RgbLzwPredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)]
[WithFile(RgbDeflate, PixelTypes.Rgba32, TiffPredictor.None)]
[WithFile(RgbDeflatePredictor, PixelTypes.Rgba32, TiffPredictor.Horizontal)]
public void TiffEncoder_PreservesPredictor<TPixel>(TestImageProvider<TPixel> provider, TiffPredictor expectedPredictor)
where TPixel : unmanaged, IPixel<TPixel>
{
// arrange
var tiffEncoder = new TiffEncoder();
using Image<TPixel> input = provider.GetImage();
using var memStream = new MemoryStream();
// act
input.Save(memStream, tiffEncoder);
// assert
memStream.Position = 0;
using var output = Image.Load<Rgba32>(Configuration, memStream);
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
Assert.Equal(expectedPredictor, (TiffPredictor)exifProfile.GetValue(ExifTag.Predictor).Value);
}
[Theory] [Theory]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.Ccitt1D, TiffCompression.Ccitt1D)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffCompression.Ccitt1D, TiffCompression.Ccitt1D)]
@ -172,15 +236,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
} }
[Theory] [Theory]
[InlineData(TiffEncodingMode.ColorPalette, TiffCompression.CcittGroup3Fax)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.CcittGroup3Fax)]
[InlineData(TiffEncodingMode.ColorPalette, TiffCompression.Ccitt1D)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Ccitt1D)]
[InlineData(TiffEncodingMode.Gray, TiffCompression.Ccitt1D)] public void TiffEncoder_IncompatibilityOptions_ThrowsImageFormatException(TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression)
[InlineData(TiffEncodingMode.Rgb, TiffCompression.Ccitt1D)]
public void TiffEncoder_IncompatibilityOptions(TiffEncodingMode mode, TiffCompression compression)
{ {
// arrange // arrange
using var input = new Image<Rgb24>(10, 10); using var input = new Image<Rgb24>(10, 10);
var encoder = new TiffEncoder() { Mode = mode, Compression = compression }; var encoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression };
using var memStream = new MemoryStream(); using var memStream = new MemoryStream();
// act // act
@ -190,154 +252,154 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb);
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate);
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Deflate, TiffPredictor.Horizontal); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffPredictor.Horizontal);
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw);
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.Lzw, TiffPredictor.Horizontal); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw, TiffPredictor.Horizontal);
[Theory] [Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.Rgb, TiffCompression.PackBits); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Deflate, TiffPredictor.Horizontal); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffPredictor.Horizontal);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.Lzw, TiffPredictor.Horizontal); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffPredictor.Horizontal);
[Theory] [Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeGray_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.Gray, TiffCompression.PackBits); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits);
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_With4Bit_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_With4Bit_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
//// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead. //// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead.
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder()); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder());
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_WithDeflateCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Deflate, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_WithLzwCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_WithLzwCompressionAndPredictor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeBiColor_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffEncodingMode.BiColor); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.BlackIsZero);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeBiColor_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Deflate); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeBiColor_WithPackBitsCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.PackBits); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffEncodingMode.BiColor, TiffCompression.Ccitt1D); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D);
[Theory] [Theory]
[WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffEncodingMode.Gray, TiffCompression.PackBits)] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)]
[WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffEncodingMode.ColorPalette, TiffCompression.Lzw)] [WithFile(PaletteDeflateMultistrip, PixelTypes.L8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw)]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.Deflate)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate)]
[WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffEncodingMode.Rgb, TiffCompression.None)] [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffPhotometricInterpretation.Rgb, TiffCompression.None)]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncodingMode.Rgb, TiffCompression.None)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.None)]
[WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffEncodingMode.Rgb, TiffCompression.None)] [WithFile(RgbUncompressed, PixelTypes.Rgb48, TiffPhotometricInterpretation.Rgb, TiffCompression.None)]
public void TiffEncoder_StripLength<TPixel>(TestImageProvider<TPixel> provider, TiffEncodingMode mode, TiffCompression compression) public void TiffEncoder_StripLength<TPixel>(TestImageProvider<TPixel> provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
TestStripLength(provider, mode, compression); TestStripLength(provider, photometricInterpretation, compression);
[Theory] [Theory]
[WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffEncodingMode.BiColor, TiffCompression.CcittGroup3Fax)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax)]
public void TiffEncoder_StripLength_OutOfBounds<TPixel>(TestImageProvider<TPixel> provider, TiffEncodingMode mode, TiffCompression compression) public void TiffEncoder_StripLength_OutOfBounds<TPixel>(TestImageProvider<TPixel> provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
//// CcittGroup3Fax compressed data length can be larger than the original length //// CcittGroup3Fax compressed data length can be larger than the original length.
Assert.Throws<Xunit.Sdk.TrueException>(() => TestStripLength(provider, mode, compression)); Assert.Throws<Xunit.Sdk.TrueException>(() => TestStripLength(provider, photometricInterpretation, compression));
private static void TestStripLength<TPixel>(TestImageProvider<TPixel> provider, TiffEncodingMode mode, TiffCompression compression) private static void TestStripLength<TPixel>(TestImageProvider<TPixel> provider, TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// arrange // arrange
var tiffEncoder = new TiffEncoder() { Mode = mode, Compression = compression }; var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression };
using Image<TPixel> input = provider.GetImage(); using Image<TPixel> input = provider.GetImage();
using var memStream = new MemoryStream(); using var memStream = new MemoryStream();
ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile; ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile;
@ -384,14 +446,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
TestTiffEncoderCore( TestTiffEncoderCore(
provider, provider,
inputMeta.BitsPerPixel, inputMeta.BitsPerPixel,
mode, photometricInterpretation,
inputCompression); inputCompression);
} }
private static void TestTiffEncoderCore<TPixel>( private static void TestTiffEncoderCore<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
TiffBitsPerPixel? bitsPerPixel, TiffBitsPerPixel? bitsPerPixel,
TiffEncodingMode mode, TiffPhotometricInterpretation photometricInterpretation,
TiffCompression compression = TiffCompression.None, TiffCompression compression = TiffCompression.None,
TiffPredictor predictor = TiffPredictor.None, TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true, bool useExactComparer = true,
@ -402,7 +464,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
var encoder = new TiffEncoder var encoder = new TiffEncoder
{ {
Mode = mode, PhotometricInterpretation = photometricInterpretation,
BitsPerPixel = bitsPerPixel, BitsPerPixel = bitsPerPixel,
Compression = compression, Compression = compression,
HorizontalPredictor = predictor HorizontalPredictor = predictor

4
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaInput.BitsPerPixel);
// Save to Tiff // Save to Tiff
var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb }; var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb };
using var ms = new MemoryStream(); using var ms = new MemoryStream();
image.Save(ms, tiffEncoder); image.Save(ms, tiffEncoder);
@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile; byte[] encodedImageXmpProfile = rootFrameEncodedImage.Metadata.XmpProfile;
Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaDataEncodedRootFrame.BitsPerPixel); Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaDataEncodedRootFrame.BitsPerPixel);
Assert.Equal(TiffCompression.None, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value); Assert.Equal(TiffCompression.Lzw, (TiffCompression)encodedImageExifProfile.GetValue(ExifTag.Compression).Value);
Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution); Assert.Equal(inputMetaData.HorizontalResolution, encodedImageMetaData.HorizontalResolution);
Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution); Assert.Equal(inputMetaData.VerticalResolution, encodedImageMetaData.VerticalResolution);

Loading…
Cancel
Save