diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
index b40647a937..031494fc5d 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
@@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
///
public enum TiffCompression : ushort
{
+ ///
+ /// A invalid compression value.
+ ///
+ Invalid = 0,
+
///
/// No compression.
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
index a0c7eb0213..33e13d08ea 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
namespace SixLabors.ImageSharp.Formats.Tiff
@@ -28,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return TiffConstants.BitsPerSampleRgb8Bit;
default:
- TiffThrowHelper.ThrowNotSupported("The bits per pixels are not supported");
return Array.Empty();
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index 75cd063613..d67ffc0693 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -148,7 +148,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
var framesMetadata = new List();
foreach (ExifProfile ifd in directories)
{
- var meta = new TiffFrameMetadata() { ExifProfile = ifd };
+ var meta = new TiffFrameMetadata(ifd);
+ meta.Initialize(ifd);
framesMetadata.Add(meta);
}
@@ -158,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
int width = GetImageWidth(root);
int height = GetImageHeight(root);
- return new ImageInfo(new PixelTypeInfo(root.BitsPerSample.BitsPerPixel()), width, height, metadata);
+ return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), width, height, metadata);
}
///
@@ -175,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
var coreMetadata = new ImageFrameMetadata();
frameMetaData = coreMetadata.GetTiffMetadata();
- frameMetaData.ExifProfile = tags;
+ frameMetaData.Initialize(tags);
this.VerifyAndParse(frameMetaData);
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
index aa44b0b035..5b67c363af 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
@@ -126,6 +126,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData)
- => (TiffBitsPerPixel)firstFrameMetaData.BitsPerSample.BitsPerPixel();
+ => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index aaf4502cdc..b9f30a3efa 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The IFD entries container to read the image format information for.
public static void VerifyAndParse(this TiffDecoderCore options, TiffFrameMetadata entries)
{
+ if (entries.TileOffsets != null)
+ {
+ TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
+ }
+
if (entries.ExtraSamples != null)
{
TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported.");
@@ -30,11 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported.");
}
- if (entries.ExifProfile.GetValue(ExifTag.TileOffsets) != null)
- {
- TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
- }
-
if (entries.Predictor == TiffPredictor.FloatingPoint)
{
TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported.");
@@ -51,16 +51,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
}
- if (entries.ExifProfile.GetValue(ExifTag.StripRowCounts) != null)
+ if (entries.StripRowCounts != null)
{
TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported.");
}
+ entries.VerifyRequiredFieldsArePresent();
+
options.PlanarConfiguration = entries.PlanarConfiguration;
options.Predictor = entries.Predictor;
options.PhotometricInterpretation = entries.PhotometricInterpretation;
- options.BitsPerSample = entries.BitsPerSample;
- options.BitsPerPixel = entries.BitsPerSample.BitsPerPixel();
+ options.BitsPerSample = entries.BitsPerSample.GetValueOrDefault();
+ options.BitsPerPixel = entries.BitsPerSample.GetValueOrDefault().BitsPerPixel();
ParseColorType(options, entries);
ParseCompression(options, entries);
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 09fdffa249..24fd465263 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
this.BitsPerPixel = options.BitsPerPixel;
this.HorizontalPredictor = options.HorizontalPredictor;
- this.CompressionType = options.Compression;
+ this.CompressionType = options.Compression != TiffCompression.Invalid ? options.Compression : TiffCompression.None;
this.compressionLevel = options.CompressionLevel;
}
@@ -113,7 +113,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
ImageMetadata metadata = image.Metadata;
TiffMetadata tiffMetadata = metadata.GetTiffMetadata();
TiffFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata.GetTiffMetadata();
- TiffPhotometricInterpretation photometricInterpretation = rootFrameMetaData.PhotometricInterpretation;
+ TiffPhotometricInterpretation photometricInterpretation = this.Mode == TiffEncodingMode.ColorPalette
+ ? TiffPhotometricInterpretation.PaletteColor : rootFrameMetaData.PhotometricInterpretation;
this.SetMode(tiffMetadata, photometricInterpretation);
this.SetBitsPerPixel(tiffMetadata);
@@ -159,31 +160,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// Write the image bytes to the steam.
uint imageDataStart = (uint)writer.Position;
- TiffBitsPerPixel? tiffBitsPerPixel = this.BitsPerPixel;
- if (tiffBitsPerPixel != null)
- {
- using TiffBaseCompressor compressor = TiffCompressorFactory.Create(
- this.CompressionType,
- writer.BaseStream,
- this.memoryAllocator,
- image.Width,
- (int)tiffBitsPerPixel,
- this.compressionLevel,
- this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None);
-
- using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(
- this.Mode,
- image.Frames.RootFrame,
- this.quantizer,
- this.memoryAllocator,
- this.configuration,
- entriesCollector,
- (int)tiffBitsPerPixel);
-
- int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow);
-
- colorWriter.Write(compressor, rowsPerStrip);
- }
+ using TiffBaseCompressor compressor = TiffCompressorFactory.Create(
+ this.CompressionType,
+ writer.BaseStream,
+ this.memoryAllocator,
+ image.Width,
+ (int)this.BitsPerPixel,
+ this.compressionLevel,
+ this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor : TiffPredictor.None);
+
+ using TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(
+ this.Mode,
+ image.Frames.RootFrame,
+ this.quantizer,
+ this.memoryAllocator,
+ this.configuration,
+ entriesCollector,
+ (int)this.BitsPerPixel);
+
+ int rowsPerStrip = this.CalcRowsPerStrip(image.Frames.RootFrame.Height, colorWriter.BytesPerRow);
+
+ colorWriter.Write(compressor, rowsPerStrip);
entriesCollector.ProcessImageFormat(this);
entriesCollector.ProcessGeneral(image);
diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
index e00fac1519..d98b3ac94a 100644
--- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
@@ -18,195 +18,201 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private const TiffPredictor DefaultPredictor = TiffPredictor.None;
- private ExifProfile frameTags;
-
///
- /// Gets the Tiff directory tags.
+ /// Initializes a new instance of the class.
///
- public ExifProfile ExifProfile
- {
- get => this.frameTags ??= new ExifProfile();
- internal set => this.frameTags = value;
- }
+ public TiffFrameMetadata() => this.Initialize(new ExifProfile());
///
- /// Gets a general indication of the kind of data contained in this subfile.
+ /// Initializes a new instance of the class.
///
- public TiffNewSubfileType SubfileType => (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage;
+ /// The Tiff frame directory tags.
+ public TiffFrameMetadata(ExifProfile frameTags) => this.Initialize(frameTags);
///
- /// Gets a general indication of the kind of data contained in this subfile.
+ /// Initializes a new instance of the class with a given ExifProfile.
///
- public TiffSubfileType? OldSubfileType => (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value;
+ /// The Tiff frame directory tags.
+ public void Initialize(ExifProfile frameTags)
+ {
+ this.ExifProfile = frameTags;
+
+ this.FillOrder = (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst;
+ this.Compression = this.ExifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)this.ExifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None;
+ this.SubfileType = (TiffNewSubfileType?)this.ExifProfile.GetValue(ExifTag.SubfileType)?.Value ?? TiffNewSubfileType.FullImage;
+ this.OldSubfileType = (TiffSubfileType?)this.ExifProfile.GetValue(ExifTag.OldSubfileType)?.Value;
+ this.HorizontalResolution = this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble();
+ this.VerticalResolution = this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble();
+ this.PlanarConfiguration = (TiffPlanarConfiguration?)this.ExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
+ this.ResolutionUnit = UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile);
+ this.ColorMap = this.ExifProfile.GetValue(ExifTag.ColorMap)?.Value;
+ this.ExtraSamples = this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value;
+ this.Predictor = (TiffPredictor?)this.ExifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor;
+ this.SampleFormat = this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
+ this.SamplesPerPixel = this.ExifProfile.GetValue(ExifTag.SamplesPerPixel)?.Value;
+ this.StripRowCounts = this.ExifProfile.GetValue(ExifTag.StripRowCounts)?.Value;
+ this.RowsPerStrip = this.ExifProfile.GetValue(ExifTag.RowsPerStrip) != null ? this.ExifProfile.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity;
+ this.TileOffsets = this.ExifProfile.GetValue(ExifTag.TileOffsets)?.Value;
+
+ this.PhotometricInterpretation = this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ?
+ (TiffPhotometricInterpretation)this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.WhiteIsZero;
+
+ // Required Fields for decoding the image.
+ this.StripOffsets = this.ExifProfile.GetValue(ExifTag.StripOffsets)?.Value;
+ this.StripByteCounts = this.ExifProfile.GetValue(ExifTag.StripByteCounts)?.Value;
+
+ ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value;
+ if (bits == null)
+ {
+ if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
+ {
+ this.BitsPerSample = TiffBitsPerSample.Bit1;
+ }
+
+ this.BitsPerSample = null;
+ }
+ else
+ {
+ this.BitsPerSample = bits.GetBitsPerSample();
+ }
+
+ this.BitsPerPixel = this.BitsPerSample.GetValueOrDefault().BitsPerPixel();
+ }
///
- /// Gets the number of bits per component.
+ /// Verifies that the required fields for decoding an image are present.
+ /// If not, a ImageFormatException will be thrown.
///
- public TiffBitsPerSample BitsPerSample
+ public void VerifyRequiredFieldsArePresent()
{
- get
+ if (this.StripOffsets == null)
{
- ushort[] bits = this.ExifProfile.GetValue(ExifTag.BitsPerSample)?.Value;
- if (bits == null)
- {
- if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero
- || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
- {
- return TiffBitsPerSample.Bit1;
- }
+ TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!");
+ }
- TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image.");
- }
+ if (this.StripByteCounts == null)
+ {
+ TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!");
+ }
- return bits.GetBitsPerSample();
+ if (this.BitsPerSample == null)
+ {
+ TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!");
}
}
///
- /// Gets the bits per pixel.
+ /// Gets the Tiff directory tags.
///
- public int BitsPerPixel => this.BitsPerSample.BitsPerPixel();
+ public ExifProfile ExifProfile { get; internal set; }
///
- /// Gets the compression scheme used on the image data.
+ /// Gets or sets a general indication of the kind of data contained in this subfile.
///
- /// The compression scheme used on the image data.
- public TiffCompression Compression
- {
- get
- {
- IExifValue compression = this.ExifProfile.GetValue(ExifTag.Compression);
- if (compression == null)
- {
- return TiffCompression.None;
- }
+ public TiffNewSubfileType? SubfileType { get; set; }
- return (TiffCompression)compression.Value;
- }
- }
+ ///
+ /// Gets or sets a general indication of the kind of data contained in this subfile.
+ ///
+ public TiffSubfileType? OldSubfileType { get; set; }
///
- /// Gets the color space of the image data.
+ /// Gets or sets the number of bits per component.
///
- public TiffPhotometricInterpretation PhotometricInterpretation
- {
- get
- {
- IExifValue photometricInterpretation = this.ExifProfile.GetValue(ExifTag.PhotometricInterpretation);
- if (photometricInterpretation == null)
- {
- return TiffPhotometricInterpretation.WhiteIsZero;
- }
+ public TiffBitsPerSample? BitsPerSample { get; set; }
- return (TiffPhotometricInterpretation)photometricInterpretation.Value;
- }
- }
+ ///
+ /// Gets or sets the bits per pixel.
+ ///
+ public int BitsPerPixel { get; set; }
///
- /// Gets the logical order of bits within a byte.
+ /// Gets or sets the compression scheme used on the image data.
///
- internal TiffFillOrder FillOrder => (TiffFillOrder?)this.ExifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst;
+ /// The compression scheme used on the image data.
+ public TiffCompression Compression { get; set; }
///
- /// Gets for each strip, the byte offset of that strip.
+ /// Gets or sets the color space of the image data.
///
- public Number[] StripOffsets
- {
- get
- {
- IExifValue stripOffsets = this.ExifProfile.GetValue(ExifTag.StripOffsets);
- if (stripOffsets == null)
- {
- TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing");
- }
+ public TiffPhotometricInterpretation PhotometricInterpretation { get; set; }
- return stripOffsets.Value;
- }
- }
+ ///
+ /// Gets or sets the logical order of bits within a byte.
+ ///
+ internal TiffFillOrder FillOrder { get; set; }
///
- /// Gets the number of components per pixel.
+ /// Gets or sets for each strip, the byte offset of that strip.
///
- public ushort SamplesPerPixel => this.ExifProfile.GetValue(ExifTag.SamplesPerPixel).Value;
+ public Number[] StripOffsets { get; set; }
///
- /// Gets the number of rows per strip.
+ /// Gets or sets the strip row counts.
///
- public Number RowsPerStrip
- {
- get
- {
- IExifValue rowsPerStrip = this.ExifProfile.GetValue(ExifTag.RowsPerStrip);
- if (rowsPerStrip == null)
- {
- return TiffConstants.RowsPerStripInfinity;
- }
+ public uint[] StripRowCounts { get; set; }
- return rowsPerStrip.Value;
- }
- }
+ ///
+ /// Gets or sets the number of components per pixel.
+ ///
+ public ushort? SamplesPerPixel { get; set; }
///
- /// Gets for each strip, the number of bytes in the strip after compression.
+ /// Gets or sets the number of rows per strip.
///
- public Number[] StripByteCounts
- {
- get
- {
- IExifValue stripByteCounts = this.ExifProfile.GetValue(ExifTag.StripByteCounts);
- if (stripByteCounts == null)
- {
- TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing");
- }
+ public Number RowsPerStrip { get; set; }
- return stripByteCounts.Value;
- }
- }
+ ///
+ /// Gets or sets for each strip, the number of bytes in the strip after compression.
+ ///
+ public Number[] StripByteCounts { get; set; }
+
+ ///
+ /// Gets or sets the resolution of the image in x-direction.
+ ///
+ public double? HorizontalResolution { get; set; }
///
- /// Gets the resolution of the image in x- direction.
+ /// Gets or sets the resolution of the image in y-direction.
///
- /// The density of the image in x- direction.
- public double? HorizontalResolution => this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble();
+ public double? VerticalResolution { get; set; }
///
- /// Gets the resolution of the image in y- direction.
+ /// Gets or sets how the components of each pixel are stored.
///
- /// The density of the image in y- direction.
- public double? VerticalResolution => this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble();
+ public TiffPlanarConfiguration PlanarConfiguration { get; set; }
///
- /// Gets how the components of each pixel are stored.
+ /// Gets or sets the unit of measurement for XResolution and YResolution.
///
- public TiffPlanarConfiguration PlanarConfiguration => (TiffPlanarConfiguration?)this.ExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
+ public PixelResolutionUnit ResolutionUnit { get; set; }
///
- /// Gets the unit of measurement for XResolution and YResolution.
+ /// Gets or sets a color map for palette color images.
///
- public PixelResolutionUnit ResolutionUnit => UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile);
+ public ushort[] ColorMap { get; set; }
///
- /// Gets a color map for palette color images.
+ /// Gets or sets the description of extra components.
///
- public ushort[] ColorMap => this.ExifProfile.GetValue(ExifTag.ColorMap)?.Value;
+ public ushort[] ExtraSamples { get; set; }
///
- /// Gets the description of extra components.
+ /// Gets or sets the tile offsets.
///
- public ushort[] ExtraSamples => this.ExifProfile.GetValue(ExifTag.ExtraSamples)?.Value;
+ public uint[] TileOffsets { get; set; }
///
- /// Gets a mathematical operator that is applied to the image data before an encoding scheme is applied.
+ /// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied.
///
- public TiffPredictor Predictor => (TiffPredictor?)this.ExifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor;
+ public TiffPredictor Predictor { get; set; }
///
- /// Gets the specifies how to interpret each data sample in a pixel.
- ///
+ /// Gets or sets the specifies how to interpret each data sample in a pixel.
///
- public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
+ public TiffSampleFormat[] SampleFormat { get; set; }
///
- public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() };
+ public IDeepCloneable DeepClone() => new TiffFrameMetadata(this.ExifProfile.DeepClone());
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index dda695568c..77098c42ca 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -254,7 +254,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[WithFile(Rgb4BitPalette, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider)
where TPixel : unmanaged, IPixel =>
- TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffEncodingMode.ColorPalette, useExactComparer: false, compareTolerance: 0.001f);
+ // 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());
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
@@ -384,8 +385,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
TiffCompression compression = TiffCompression.None,
TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true,
- int maxStripSize = 0,
- float compareTolerance = 0.01f)
+ float compareTolerance = 0.01f,
+ IImageDecoder imageDecoder = null)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage();
@@ -398,7 +399,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
};
// Does DebugSave & load reference CompareToReferenceInput():
- image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder);
+ image.VerifyEncoder(
+ provider,
+ "tiff",
+ bitsPerPixel,
+ encoder,
+ useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance),
+ referenceDecoder: imageDecoder ?? ReferenceDecoder);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
index 92412234bf..45b53eae8b 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
}
[Fact]
- public void CloneIsDeep()
+ public void TiffMetadata_CloneIsDeep()
{
byte[] xmpData = { 1, 1, 1 };
var meta = new TiffMetadata
@@ -55,6 +56,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.True(meta.XmpProfile.SequenceEqual(clone.XmpProfile));
}
+ [Theory]
+ [WithFile(SampleMetadata, PixelTypes.Rgba32)]
+ public void TiffFrameMetadata_CloneIsDeep(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image image = provider.GetImage(TiffDecoder))
+ {
+ TiffFrameMetadata meta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
+ var cloneSameAsSampleMetaData = (TiffFrameMetadata)meta.DeepClone();
+ VerifyExpectedFrameMetaDataIsPresent(cloneSameAsSampleMetaData);
+
+ var clone = (TiffFrameMetadata)meta.DeepClone();
+
+ clone.BitsPerSample = TiffBitsPerSample.Bit1;
+ clone.ColorMap = new ushort[] { 1, 2, 3 };
+
+ Assert.False(meta.BitsPerSample == clone.BitsPerSample);
+ Assert.False(meta.ColorMap.SequenceEqual(clone.ColorMap));
+ }
+ }
+
[Theory]
[InlineData(Calliphora_BiColorUncompressed, TiffBitsPerPixel.Bit1)]
[InlineData(GrayscaleUncompressed, TiffBitsPerPixel.Bit8)]
@@ -130,53 +152,63 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using (Image image = provider.GetImage(TiffDecoder))
{
- TiffMetadata meta = image.Metadata.GetTiffMetadata();
-
- Assert.NotNull(meta);
- Assert.Equal(ByteOrder.LittleEndian, meta.ByteOrder);
- Assert.Equal(PixelResolutionUnit.PixelsPerInch, image.Metadata.ResolutionUnits);
- Assert.Equal(10, image.Metadata.HorizontalResolution);
- Assert.Equal(10, image.Metadata.VerticalResolution);
-
- TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();
ImageFrame rootFrame = image.Frames.RootFrame;
- Assert.Equal(30, frameMetadata.ExifProfile.Values.Count);
-
Assert.Equal(32, rootFrame.Width);
Assert.Equal(32, rootFrame.Height);
- Assert.Equal(TiffBitsPerSample.Bit4, frameMetadata.BitsPerSample);
- Assert.Equal(TiffCompression.Lzw, frameMetadata.Compression);
- Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetadata.PhotometricInterpretation);
- Assert.Equal("This is Название", frameMetadata.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
- Assert.Equal("This is Изготовитель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Make).Value);
- Assert.Equal("This is Модель камеры", frameMetadata.ExifProfile.GetValue(ExifTag.Model).Value);
- Assert.Equal(new Number[] { 8u }, frameMetadata.StripOffsets, new NumberComparer());
- Assert.Equal(1, frameMetadata.SamplesPerPixel);
- Assert.Equal(32u, frameMetadata.RowsPerStrip);
- Assert.Equal(new Number[] { 297u }, frameMetadata.StripByteCounts, new NumberComparer());
- Assert.Equal(PixelResolutionUnit.PixelsPerInch, frameMetadata.ResolutionUnit);
- Assert.Equal(10, frameMetadata.HorizontalResolution);
- Assert.Equal(10, frameMetadata.VerticalResolution);
- Assert.Equal(TiffPlanarConfiguration.Chunky, frameMetadata.PlanarConfiguration);
- Assert.Equal("IrfanView", frameMetadata.ExifProfile.GetValue(ExifTag.Software).Value);
- Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
- Assert.Equal("This is author1;Author2", frameMetadata.ExifProfile.GetValue(ExifTag.Artist).Value);
- Assert.Null(frameMetadata.ExifProfile.GetValue(ExifTag.HostComputer)?.Value);
- Assert.Equal(48, frameMetadata.ColorMap.Length);
- Assert.Equal(10537, frameMetadata.ColorMap[0]);
- Assert.Equal(14392, frameMetadata.ColorMap[1]);
- Assert.Equal(58596, frameMetadata.ColorMap[46]);
- Assert.Equal(3855, frameMetadata.ColorMap[47]);
-
- Assert.Null(frameMetadata.ExtraSamples);
- Assert.Equal(TiffPredictor.None, frameMetadata.Predictor);
- Assert.Null(frameMetadata.SampleFormat);
- Assert.Equal("This is Авторские права", frameMetadata.ExifProfile.GetValue(ExifTag.Copyright).Value);
- Assert.Equal(4, frameMetadata.ExifProfile.GetValue(ExifTag.Rating).Value);
- Assert.Equal(75, frameMetadata.ExifProfile.GetValue(ExifTag.RatingPercent).Value);
+
+ TiffFrameMetadata frameMetaData = rootFrame.Metadata.GetTiffMetadata();
+ Assert.NotNull(frameMetaData);
+
+ ImageMetadata imageMetaData = image.Metadata;
+ Assert.NotNull(imageMetaData);
+ Assert.Equal(PixelResolutionUnit.PixelsPerInch, imageMetaData.ResolutionUnits);
+ Assert.Equal(10, imageMetaData.HorizontalResolution);
+ Assert.Equal(10, imageMetaData.VerticalResolution);
+
+ TiffMetadata tiffMetaData = image.Metadata.GetTiffMetadata();
+ Assert.NotNull(tiffMetaData);
+ Assert.Equal(ByteOrder.LittleEndian, tiffMetaData.ByteOrder);
+ Assert.Equal(TiffBitsPerPixel.Bit4, tiffMetaData.BitsPerPixel);
+
+ VerifyExpectedFrameMetaDataIsPresent(frameMetaData);
}
}
+ private static void VerifyExpectedFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData)
+ {
+ Assert.Equal(30, frameMetaData.ExifProfile.Values.Count);
+ Assert.Equal(TiffBitsPerSample.Bit4, frameMetaData.BitsPerSample);
+ Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression);
+ Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation);
+ Assert.Equal("This is Название", frameMetaData.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
+ Assert.Equal("This is Изготовитель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Make).Value);
+ Assert.Equal("This is Модель камеры", frameMetaData.ExifProfile.GetValue(ExifTag.Model).Value);
+ Assert.Equal(new Number[] {8u}, frameMetaData.StripOffsets, new NumberComparer());
+ Assert.Equal(1, frameMetaData.SamplesPerPixel.GetValueOrDefault());
+ Assert.Equal(32u, frameMetaData.RowsPerStrip);
+ Assert.Equal(new Number[] {297u}, frameMetaData.StripByteCounts, new NumberComparer());
+ Assert.Equal(PixelResolutionUnit.PixelsPerInch, frameMetaData.ResolutionUnit);
+ Assert.Equal(10, frameMetaData.HorizontalResolution);
+ Assert.Equal(10, frameMetaData.VerticalResolution);
+ Assert.Equal(TiffPlanarConfiguration.Chunky, frameMetaData.PlanarConfiguration);
+ Assert.Equal("IrfanView", frameMetaData.ExifProfile.GetValue(ExifTag.Software).Value);
+ Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
+ Assert.Equal("This is author1;Author2", frameMetaData.ExifProfile.GetValue(ExifTag.Artist).Value);
+ Assert.Null(frameMetaData.ExifProfile.GetValue(ExifTag.HostComputer)?.Value);
+ Assert.Equal(48, frameMetaData.ColorMap.Length);
+ Assert.Equal(10537, frameMetaData.ColorMap[0]);
+ Assert.Equal(14392, frameMetaData.ColorMap[1]);
+ Assert.Equal(58596, frameMetaData.ColorMap[46]);
+ Assert.Equal(3855, frameMetaData.ColorMap[47]);
+
+ Assert.Null(frameMetaData.ExtraSamples);
+ Assert.Equal(TiffPredictor.None, frameMetaData.Predictor);
+ Assert.Null(frameMetaData.SampleFormat);
+ Assert.Equal("This is Авторские права", frameMetaData.ExifProfile.GetValue(ExifTag.Copyright).Value);
+ Assert.Equal(4, frameMetaData.ExifProfile.GetValue(ExifTag.Rating).Value);
+ Assert.Equal(75, frameMetaData.ExifProfile.GetValue(ExifTag.RatingPercent).Value);
+ }
+
[Theory]
[WithFile(MultiframeDeflateWithPreview, PixelTypes.Rgba32)]
public void SubfileType(TestImageProvider provider)
@@ -204,9 +236,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
}
[Theory]
- [WithFile(SampleMetadata, PixelTypes.Rgba32, true)]
- [WithFile(SampleMetadata, PixelTypes.Rgba32, false)]
- public void PreserveMetadata(TestImageProvider provider, bool preserveMetadata)
+ [WithFile(SampleMetadata, PixelTypes.Rgba32)]
+ public void Encode_PreservesMetadata(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
// Load Tiff image
@@ -222,11 +253,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// Save to Tiff
var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb };
- if (!preserveMetadata)
- {
- ClearMeta(image);
- }
-
using var ms = new MemoryStream();
image.Save(ms, tiffEncoder);
@@ -252,142 +278,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(frameMetaInput.HorizontalResolution, tiffMetaDataEncodedRootFrame.HorizontalResolution);
Assert.Equal(frameMetaInput.VerticalResolution, tiffMetaDataEncodedRootFrame.VerticalResolution);
- if (preserveMetadata)
- {
- Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile);
-
- Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value);
- Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
- Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value);
- Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value);
-
- Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
- Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value);
- Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright).Value);
- }
- else
- {
- Assert.Null(tiffMetaDataEncodedImage.XmpProfile);
-
- Assert.Equal("ImageSharp", tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Software).Value);
- Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Software)?.Value);
- Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value);
- Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Make)?.Value);
- Assert.Null(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright)?.Value);
-
- Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value);
- Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make)?.Value);
- Assert.Null(tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright)?.Value);
- }
- }
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void CreateMetadata(bool preserveMetadata)
- {
- // Create image
- int w = 10;
- int h = 20;
- using Image image = new Image(w, h);
-
- // set metadata
- ImageMetadata coreMeta = image.Metadata;
- TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata();
- TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
-
- tiffMeta.XmpProfile = new byte[] { 1, 2, 3, 4, 5 };
-
- coreMeta.IptcProfile = new IptcProfile();
- coreMeta.IptcProfile.SetValue(IptcTag.Caption, "iptc caption");
-
- coreMeta.IccProfile = new IccProfile(new IccProfileHeader() { CreationDate = DateTime.Now }, new IccTagDataEntry[] { new IccTextTagDataEntry("test string"), new IccDataTagDataEntry(new byte[] { 11, 22, 33, 44 }) });
-
- coreMeta.ResolutionUnits = PixelResolutionUnit.PixelsPerMeter;
- coreMeta.HorizontalResolution = 4500;
- coreMeta.VerticalResolution = 5400;
-
- var datetime = DateTime.Now.ToString(CultureInfo.InvariantCulture);
- frameMeta.ExifProfile.SetValue(ExifTag.ImageDescription, "test ImageDescription");
- frameMeta.ExifProfile.SetValue(ExifTag.DateTime, datetime);
-
- // Save to Tiff
- var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffCompression.Deflate };
- if (!preserveMetadata)
- {
- ClearMeta(image);
- }
-
- using var ms = new MemoryStream();
- image.Save(ms, tiffEncoder);
-
- // Assert
- ms.Position = 0;
- using var output = Image.Load(this.configuration, ms);
- ImageFrame rootFrameOut = output.Frames.RootFrame;
-
- ImageMetadata coreMetaOut = output.Metadata;
- TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata();
- TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata();
-
- Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, coreMetaOut.ResolutionUnits);
- Assert.Equal(45, coreMetaOut.HorizontalResolution);
- Assert.Equal(54, coreMetaOut.VerticalResolution, 8);
-
- //// Assert.Equal(tiffEncoder.Compression, tiffMetaOut.Compression);
- Assert.Equal(TiffBitsPerPixel.Bit24, tiffMetaOut.BitsPerPixel);
-
- Assert.Equal(w, rootFrameOut.Width);
- Assert.Equal(h, rootFrameOut.Height);
- Assert.Equal(frameMeta.ResolutionUnit, frameMetaOut.ResolutionUnit);
- Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution);
- Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution);
-
- Assert.Equal("ImageSharp", frameMetaOut.ExifProfile.GetValue(ExifTag.Software)?.Value);
-
- if (preserveMetadata)
- {
- Assert.NotNull(tiffMeta.XmpProfile);
- Assert.NotNull(coreMeta.IptcProfile);
- Assert.NotNull(coreMeta.IccProfile);
-
- Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile);
- Assert.Equal(coreMeta.IptcProfile.Data, coreMetaOut.IptcProfile.Data);
- Assert.Equal(coreMeta.IccProfile.ToByteArray(), coreMetaOut.IccProfile.ToByteArray());
-
- Assert.Equal("test ImageDescription", frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
- Assert.Equal(datetime, frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
-
- Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
- Assert.Equal(frameMeta.ExifProfile.GetValue(ExifTag.DateTime).Value, frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime).Value);
- }
- else
- {
- Assert.Null(tiffMetaOut.XmpProfile);
- Assert.Null(coreMetaOut.IptcProfile);
- Assert.Null(coreMetaOut.IccProfile);
-
- Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value);
- Assert.Null(frameMeta.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
-
- Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.ImageDescription)?.Value);
- Assert.Null(frameMetaOut.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
- }
- }
-
- private static void ClearMeta(Image image)
- {
- ImageMetadata coreMeta = image.Metadata;
- TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata();
- TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
-
- coreMeta.ExifProfile = null;
- coreMeta.IccProfile = null;
- coreMeta.IptcProfile = null;
+ Assert.Equal(tiffMetaInput.XmpProfile, tiffMetaDataEncodedImage.XmpProfile);
- tiffMeta.XmpProfile = null;
+ Assert.Equal("IrfanView", frameMetaInput.ExifProfile.GetValue(ExifTag.Software).Value);
+ Assert.Equal("This is Название", frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
+ Assert.Equal("This is Изготовитель камеры", frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value);
+ Assert.Equal("This is Авторские права", frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value);
- frameMeta.ExifProfile = null;
+ Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.ImageDescription).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.ImageDescription).Value);
+ Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Make).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Make).Value);
+ Assert.Equal(frameMetaInput.ExifProfile.GetValue(ExifTag.Copyright).Value, tiffMetaDataEncodedRootFrame.ExifProfile.GetValue(ExifTag.Copyright).Value);
}
}
}
diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
index 208222a857..fef890a65e 100644
--- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
+++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
@@ -478,7 +478,7 @@ namespace SixLabors.ImageSharp.Tests
case TestImageWriteFormat.Png:
return WriteAndReadPng(image);
default:
- throw new ArgumentException("Unexpected test image format, only Jpeg, Png and Tiff are allowed");
+ throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed");
}
}
@@ -506,18 +506,6 @@ namespace SixLabors.ImageSharp.Tests
}
}
- private static Image WriteAndReadTiff(Image image)
- {
- using (var memStream = new MemoryStream())
- {
- image.SaveAsTiff(memStream, new TiffEncoder());
- image.Dispose();
-
- memStream.Position = 0;
- return Image.Load(memStream, new TiffDecoder());
- }
- }
-
private static void TestProfile(ExifProfile profile)
{
Assert.NotNull(profile);