diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
index 108b6ae6e..10ac39747 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
///
/// Methods for undoing the horizontal prediction used in combination with deflate and LZW compressed TIFF images.
///
- public static class HorizontalPredictor
+ internal static class HorizontalPredictor
{
///
/// Inverts the horizontal prediction.
diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
index a614235be..f7b6200fa 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
@@ -5,7 +5,7 @@ using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
+
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
@@ -673,27 +673,27 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression
{
case 4:
{
- return WhiteLen4TermCodes.Keys.Contains(this.value);
+ return WhiteLen4TermCodes.ContainsKey(this.value);
}
case 5:
{
- return WhiteLen5TermCodes.Keys.Contains(this.value);
+ return WhiteLen5TermCodes.ContainsKey(this.value);
}
case 6:
{
- return WhiteLen6TermCodes.Keys.Contains(this.value);
+ return WhiteLen6TermCodes.ContainsKey(this.value);
}
case 7:
{
- return WhiteLen7TermCodes.Keys.Contains(this.value);
+ return WhiteLen7TermCodes.ContainsKey(this.value);
}
case 8:
{
- return WhiteLen8TermCodes.Keys.Contains(this.value);
+ return WhiteLen8TermCodes.ContainsKey(this.value);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs
deleted file mode 100644
index 0fab224de..000000000
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants
-{
- ///
- /// Enumeration representing the resolution units defined by the Tiff file-format.
- ///
- public enum TiffResolutionUnit : ushort
- {
- ///
- /// No absolute unit of measurement.
- ///
- None = 1,
-
- ///
- /// Inch.
- ///
- Inch = 2,
-
- ///
- /// Centimeter.
- ///
- Centimeter = 3
- }
-}
diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
index 37492ea31..481463136 100644
--- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
@@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
internal interface ITiffEncoderOptions
{
+ ///
+ /// Gets the byte order to use.
+ ///
+ ByteOrder ByteOrder { get; }
+
///
/// Gets the compression type to use.
///
@@ -36,10 +41,5 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// Gets the quantizer for creating a color palette image.
///
IQuantizer Quantizer { get; }
-
- ///
- /// Gets a value indicating whether preserve metadata.
- ///
- bool PreserveMetadata { get; }
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
index b8532e0c5..da2f96211 100644
--- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs
@@ -116,6 +116,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
private bool ReadValueOrOffset(ExifValue entry, ExifDataType rawDataType, uint count)
{
+ if (entry.Tag == ExifTag.SubIFDOffset
+ || entry.Tag == ExifTag.GPSIFDOffset
+ /*|| entry.Tag == ExifTagValue.SubIFDs*/)
+ {
+ // todo: ignore subIfds (exif, gps)
+ this.stream.Skip(4);
+ return false;
+ }
+
if (HasExtData(entry, count))
{
uint offset = this.stream.ReadUInt32();
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
index 0e314e6ee..ea53fa061 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
@@ -13,6 +13,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
Pixel1 = 1,
+ ///
+ /// 4 bits per pixel, grayscale or indexed image. Each pixel consists of 4 bit.
+ ///
+ Pixel4 = 4,
+
///
/// 8 bits per pixel, grayscale or indexed image. Each pixel consists of 1 byte.
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index b4350c287..c44854081 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -1,10 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Threading;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
@@ -38,16 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
private readonly bool ignoreMetadata;
- ///
- /// The image metadata.
- ///
- private ImageMetadata metadata;
-
- ///
- /// The tiff specific metadata.
- ///
- private TiffMetadata tiffMetaData;
-
///
/// The stream to decode from.
///
@@ -72,6 +60,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public ushort[] BitsPerSample { get; set; }
+ public int ChunkyBitsPerPixel { get; set; }
+
///
/// Gets or sets the lookup table for RGB palette colored images.
///
@@ -97,6 +87,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public TiffPhotometricInterpretation PhotometricInterpretation { get; set; }
+ ///
+ /// Gets or sets the predictor.
+ ///
+ public TiffPredictor Predictor { get; set; }
+
///
public Configuration Configuration => this.configuration;
@@ -122,12 +117,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
framesMetadata.Add(frameMetadata);
}
- this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, tiffStream.ByteOrder);
- this.SetTiffFormatMetaData(framesMetadata, tiffStream.ByteOrder);
+ ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, tiffStream.ByteOrder);
// todo: tiff frames can have different sizes
{
- ImageFrame root = frames.First();
+ ImageFrame root = frames[0];
this.Dimensions = root.Size();
foreach (ImageFrame frame in frames)
{
@@ -138,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
- var image = new Image(this.configuration, this.metadata, frames);
+ var image = new Image(this.configuration, metadata, frames);
return image;
}
@@ -160,22 +154,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
framesMetadata.Add(meta);
}
- this.SetTiffFormatMetaData(framesMetadata, tiffStream.ByteOrder);
-
- TiffFrameMetadata root = framesMetadata.First();
- int bitsPerPixel = 0;
- foreach (var bits in root.BitsPerSample)
- {
- bitsPerPixel += bits;
- }
-
- return new ImageInfo(new PixelTypeInfo(bitsPerPixel), (int)root.Width, (int)root.Height, this.metadata);
- }
+ ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.ignoreMetadata, tiffStream.ByteOrder);
- private void SetTiffFormatMetaData(List framesMetadata, ByteOrder byteOrder)
- {
- this.metadata = framesMetadata.CreateMetadata(this.ignoreMetadata, byteOrder);
- this.tiffMetaData = this.metadata.GetTiffMetadata();
+ TiffFrameMetadata root = framesMetadata[0];
+ return new ImageInfo(new PixelTypeInfo(root.BitsPerPixel), (int)root.Width, (int)root.Height, metadata);
}
private static TiffStream CreateStream(Stream stream)
@@ -224,10 +206,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
var coreMetadata = new ImageFrameMetadata();
frameMetaData = coreMetadata.GetTiffMetadata();
frameMetaData.FrameTags.AddRange(tags);
- TiffFrameMetadata tiffFormatMetaData = coreMetadata.GetFormatMetadata(TiffFormat.Instance);
- TiffPredictor predictor = tiffFormatMetaData.Predictor;
- this.VerifyAndParseOptions(frameMetaData);
+ this.VerifyAndParse(frameMetaData);
int width = (int)frameMetaData.Width;
int height = (int)frameMetaData.Height;
@@ -239,11 +219,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar)
{
- this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, width, predictor);
+ this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, width);
}
else
{
- this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width, predictor);
+ this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, width);
}
return frame;
@@ -258,22 +238,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// The size (in bytes) of the required pixel buffer.
private int CalculateStripBufferSize(int width, int height, int plane = -1)
{
- uint bitsPerPixel = 0;
+ int bitsPerPixel = 0;
if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
DebugGuard.IsTrue(plane == -1, "Excepted Chunky planar.");
- for (int i = 0; i < this.BitsPerSample.Length; i++)
- {
- bitsPerPixel += this.BitsPerSample[i];
- }
+ bitsPerPixel = this.ChunkyBitsPerPixel;
}
else
{
bitsPerPixel = this.BitsPerSample[plane];
}
- int bytesPerRow = ((width * (int)bitsPerPixel) + 7) / 8;
+ int bytesPerRow = ((width * bitsPerPixel) + 7) / 8;
int stripBytes = bytesPerRow * height;
return stripBytes;
@@ -288,17 +265,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// An array of byte offsets to each strip in the image.
/// An array of the size of each strip (in bytes).
/// The image width.
- /// The tiff predictor used.
- private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width, TiffPredictor predictor)
+ private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width)
where TPixel : unmanaged, IPixel
{
int stripsPerPixel = this.BitsPerSample.Length;
int stripsPerPlane = stripOffsets.Length / stripsPerPixel;
- int bitsPerPixel = 0;
- foreach (var bits in this.BitsPerSample)
- {
- bitsPerPixel += bits;
- }
+ int bitsPerPixel = this.ChunkyBitsPerPixel; // todo?
Buffer2D pixels = frame.PixelBuffer;
@@ -312,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize);
}
- TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, predictor);
+ TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor);
RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap);
@@ -339,21 +311,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
- private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width, TiffPredictor predictor)
+ private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts, int width)
where TPixel : unmanaged, IPixel
{
int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip);
- int bitsPerPixel = 0;
- foreach (var bits in this.BitsPerSample)
- {
- bitsPerPixel += bits;
- }
+ int bitsPerPixel = this.ChunkyBitsPerPixel;
using IManagedByteBuffer stripBuffer = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize, AllocationOptions.Clean);
Buffer2D pixels = frame.PixelBuffer;
- TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, predictor);
+ TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor);
TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap);
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
similarity index 62%
rename from src/ImageSharp/Formats/Tiff/TiffDecoderMetadataHelpers.cs
rename to src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
index 0eddca4bb..99337a8b2 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataHelpers.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
@@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
-using System.Linq;
-using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -13,28 +11,21 @@ using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
///
- /// The decoder metadata helper methods.
+ /// The decoder metadata creator.
///
- internal static class TiffDecoderMetadataHelpers
+ internal static class TiffDecoderMetadataCreator
{
- public static ImageMetadata CreateMetadata(this IList frames, bool ignoreMetadata, ByteOrder byteOrder)
+ public static ImageMetadata Create(List frames, bool ignoreMetadata, ByteOrder byteOrder)
{
- var coreMetadata = new ImageMetadata();
-
- TiffFrameMetadata rootFrameMetadata = frames.First();
- switch (rootFrameMetadata.ResolutionUnit)
+ if (frames.Count < 1)
{
- case TiffResolutionUnit.None:
- coreMetadata.ResolutionUnits = PixelResolutionUnit.AspectRatio;
- break;
- case TiffResolutionUnit.Inch:
- coreMetadata.ResolutionUnits = PixelResolutionUnit.PixelsPerInch;
- break;
- case TiffResolutionUnit.Centimeter:
- coreMetadata.ResolutionUnits = PixelResolutionUnit.PixelsPerCentimeter;
- break;
+ TiffThrowHelper.ThrowImageFormatException("Expected at least one frame.");
}
+ var coreMetadata = new ImageMetadata();
+ TiffFrameMetadata rootFrameMetadata = frames[0];
+
+ coreMetadata.ResolutionUnits = rootFrameMetadata.ResolutionUnit;
if (rootFrameMetadata.HorizontalResolution != null)
{
coreMetadata.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value;
@@ -63,6 +54,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
+ if (coreMetadata.ExifProfile == null)
+ {
+ byte[] buf = frame.GetArray(ExifTag.SubIFDOffset, true);
+ if (buf != null)
+ {
+ coreMetadata.ExifProfile = new ExifProfile(buf);
+ }
+ }
+
if (coreMetadata.IptcProfile == null)
{
byte[] buf = frame.GetArray(ExifTag.IPTC, true);
@@ -87,28 +87,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData)
- {
- ushort[] bitsPerSample = firstFrameMetaData.BitsPerSample;
- var bitsPerPixel = 0;
- foreach (var bps in bitsPerSample)
- {
- bitsPerPixel += bps;
- }
-
- if (bitsPerPixel == 24)
- {
- return TiffBitsPerPixel.Pixel24;
- }
- else if (bitsPerPixel == 8)
- {
- return TiffBitsPerPixel.Pixel8;
- }
- else if (bitsPerPixel == 1)
- {
- return TiffBitsPerPixel.Pixel1;
- }
-
- return 0;
- }
+ => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
similarity index 89%
rename from src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs
rename to src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index 7fd76d04d..e81e51946 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderHelpers.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -8,16 +8,16 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
///
- /// The decoder helper methods.
+ /// The decoder options parser.
///
- internal static class TiffDecoderHelpers
+ internal static class TiffDecoderOptionsParser
{
///
/// Determines the TIFF compression and color types, and reads any associated parameters.
///
/// The options.
/// The IFD entries container to read the image format information for.
- public static void VerifyAndParseOptions(this TiffDecoderCore options, TiffFrameMetadata entries)
+ public static void VerifyAndParse(this TiffDecoderCore options, TiffFrameMetadata entries)
{
if (entries.ExtraSamples != null)
{
@@ -50,15 +50,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
- ParseCompression(options, entries.Compression);
-
options.PlanarConfiguration = entries.PlanarConfiguration;
+ options.Predictor = entries.Predictor;
- ParsePhotometric(options, entries);
-
- ParseBitsPerSample(options, entries);
+ // todo: There is no default for PhotometricInterpretation, and it is required.
+ options.PhotometricInterpretation = entries.PhotometricInterpretation;
+ options.BitsPerSample = entries.BitsPerSample;
+ options.ChunkyBitsPerPixel = entries.BitsPerPixel;
ParseColorType(options, entries);
+ ParseCompression(options, entries.Compression);
}
private static void ParseColorType(this TiffDecoderCore options, TiffFrameMetadata entries)
@@ -209,29 +210,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
- private static void ParseBitsPerSample(this TiffDecoderCore options, TiffFrameMetadata entries)
- {
- options.BitsPerSample = entries.BitsPerSample;
- if (options.BitsPerSample == null)
- {
- if (options.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero
- || options.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
- {
- options.BitsPerSample = new[] { (ushort)1 };
- }
- else
- {
- TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing.");
- }
- }
- }
-
- private static void ParsePhotometric(this TiffDecoderCore options, TiffFrameMetadata entries)
- {
- // There is no default for PhotometricInterpretation, and it is required.
- options.PhotometricInterpretation = entries.PhotometricInterpretation;
- }
-
private static void ParseCompression(this TiffDecoderCore options, TiffCompression compression)
{
switch (compression)
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
index 1ed416878..cddc962fc 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
@@ -17,6 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public class TiffEncoder : IImageEncoder, ITiffEncoderOptions
{
+ ///
+ public ByteOrder ByteOrder { get; } = TiffEncoderCore.ByteOrder;
+
///
public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None;
@@ -32,9 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public IQuantizer Quantizer { get; set; }
- ///
- public bool PreserveMetadata { get; set; }
-
///
public void Encode(Image image, Stream stream)
where TPixel : unmanaged, IPixel
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 44a3140de..c7301049d 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -24,6 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
internal sealed class TiffEncoderCore : IImageEncoderInternals
{
+ public static readonly ByteOrder ByteOrder = BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
+
+ private static readonly ushort ByteOrderMarker = BitConverter.IsLittleEndian
+ ? TiffConstants.ByteOrderLittleEndianShort
+ : TiffConstants.ByteOrderBigEndianShort;
+
///
/// Used for allocating memory during processing operations.
///
@@ -54,8 +60,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
private readonly DeflateCompressionLevel compressionLevel;
- private readonly bool preserveMetadata;
-
///
/// Initializes a new instance of the class.
///
@@ -69,7 +73,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
this.useHorizontalPredictor = options.UseHorizontalPredictor;
this.compressionLevel = options.CompressionLevel;
- this.preserveMetadata = options.PreserveMetadata;
}
///
@@ -137,12 +140,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// The marker to write the first IFD offset.
public long WriteHeader(TiffWriter writer)
{
- ushort byteOrderMarker = BitConverter.IsLittleEndian
- ? TiffConstants.ByteOrderLittleEndianShort
- : TiffConstants.ByteOrderBigEndianShort;
-
- writer.Write(byteOrderMarker);
- writer.Write((ushort)42);
+ writer.Write(ByteOrderMarker);
+ writer.Write((ushort)TiffConstants.HeaderMagicNumber);
long firstIfdMarker = writer.PlaceMarker();
return firstIfdMarker;
@@ -182,7 +181,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.AddStripTags(image, entriesCollector, imageDataStart, imageDataBytes);
entriesCollector.ProcessImageFormat(this);
- entriesCollector.ProcessGeneral(image, this.preserveMetadata);
+ entriesCollector.ProcessGeneral(image);
writer.WriteMarker(ifdOffset, (uint)writer.Position);
long nextIfdMarker = this.WriteIfd(writer, entriesCollector.Entries);
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
index 656ce858b..41d833299 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
@@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
public List Entries { get; } = new List();
- public void ProcessGeneral(Image image, bool preserveMetadata)
+ public void ProcessGeneral(Image image)
where TPixel : unmanaged, IPixel
- => new GeneralProcessor(this).Process(image, preserveMetadata);
+ => new GeneralProcessor(this).Process(image);
public void ProcessImageFormat(TiffEncoderCore encoder)
=> new ImageFormatProcessor(this).Process(encoder);
@@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
public GeneralProcessor(TiffEncoderEntriesCollector collector) => this.collector = collector;
- public void Process(Image image, bool preserveMetadata)
+ public void Process(Image image)
where TPixel : unmanaged, IPixel
{
TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();
@@ -67,15 +67,51 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.ProcessResolution(image.Metadata, frameMetadata);
- if (preserveMetadata)
+ this.ProcessProfiles(image.Metadata, frameMetadata);
+ this.ProcessMetadata(frameMetadata);
+ }
+
+ private static bool IsMetadata(ExifTag tag)
+ {
+ switch ((ExifTagValue)(ushort)tag)
{
- this.ProcessMetadata(frameMetadata);
+ case ExifTagValue.DocumentName:
+ case ExifTagValue.ImageDescription:
+ case ExifTagValue.Make:
+ case ExifTagValue.Model:
+ case ExifTagValue.Software:
+ case ExifTagValue.DateTime:
+ case ExifTagValue.Artist:
+ case ExifTagValue.HostComputer:
+ case ExifTagValue.TargetPrinter:
+ case ExifTagValue.XMP:
+ case ExifTagValue.Rating:
+ case ExifTagValue.RatingPercent:
+ case ExifTagValue.ImageID:
+ case ExifTagValue.Copyright:
+ case ExifTagValue.MDLabName:
+ case ExifTagValue.MDSampleInfo:
+ case ExifTagValue.MDPrepDate:
+ case ExifTagValue.MDPrepTime:
+ case ExifTagValue.MDFileUnits:
+ case ExifTagValue.SEMInfo:
+ case ExifTagValue.XPTitle:
+ case ExifTagValue.XPComment:
+ case ExifTagValue.XPAuthor:
+ case ExifTagValue.XPKeywords:
+ case ExifTagValue.XPSubject:
+ return true;
+ default:
+ return false;
}
}
private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata)
{
- SynchResolution(imageMetadata, frameMetadata);
+ frameMetadata.SetResolutions(
+ imageMetadata.ResolutionUnits,
+ imageMetadata.HorizontalResolution,
+ imageMetadata.VerticalResolution);
var xResolution = new ExifRational(ExifTagValue.XResolution)
{
@@ -107,6 +143,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
continue;
}
+ switch ((ExifTagValue)(ushort)entry.Tag)
+ {
+ case ExifTagValue.SubIFDOffset:
+ case ExifTagValue.GPSIFDOffset:
+ case ExifTagValue.SubIFDs:
+ case ExifTagValue.XMP:
+ case ExifTagValue.IPTC:
+ case ExifTagValue.IccProfile:
+ continue;
+ }
+
switch (ExifTags.GetPart(entry.Tag))
{
case ExifParts.ExifTags:
@@ -129,71 +176,59 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
}
- private static void SynchResolution(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata)
+ private void ProcessProfiles(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata)
{
- double xres = imageMetadata.HorizontalResolution;
- double yres = imageMetadata.VerticalResolution;
+ if (imageMetadata.ExifProfile != null)
+ {
+ // todo: implement processing exif profile
+ }
+ else
+ {
+ tiffFrameMetadata.Remove(ExifTag.SubIFDOffset);
+ }
- switch (imageMetadata.ResolutionUnits)
+ if (imageMetadata.IptcProfile != null)
{
- case PixelResolutionUnit.AspectRatio:
- tiffFrameMetadata.ResolutionUnit = TiffResolutionUnit.None;
- break;
- case PixelResolutionUnit.PixelsPerInch:
- tiffFrameMetadata.ResolutionUnit = TiffResolutionUnit.Inch;
- break;
- case PixelResolutionUnit.PixelsPerCentimeter:
- tiffFrameMetadata.ResolutionUnit = TiffResolutionUnit.Centimeter;
- break;
- case PixelResolutionUnit.PixelsPerMeter:
+ imageMetadata.IptcProfile.UpdateData();
+ var iptc = new ExifByteArray(ExifTagValue.IPTC, ExifDataType.Byte)
{
- tiffFrameMetadata.ResolutionUnit = TiffResolutionUnit.Centimeter;
- xres = UnitConverter.MeterToCm(xres);
- yres = UnitConverter.MeterToCm(yres);
- }
+ Value = imageMetadata.IptcProfile.Data
+ };
- break;
- default:
- tiffFrameMetadata.ResolutionUnit = TiffResolutionUnit.None;
- break;
+ this.collector.AddInternal(iptc);
+ }
+ else
+ {
+ tiffFrameMetadata.Remove(ExifTag.IPTC);
}
- tiffFrameMetadata.HorizontalResolution = xres;
- tiffFrameMetadata.VerticalResolution = yres;
- }
+ if (imageMetadata.IccProfile != null)
+ {
+ var icc = new ExifByteArray(ExifTagValue.IccProfile, ExifDataType.Undefined)
+ {
+ Value = imageMetadata.IccProfile.ToByteArray()
+ };
- private static bool IsMetadata(ExifTag tag)
- {
- switch ((ExifTagValue)(ushort)tag)
+ this.collector.AddInternal(icc);
+ }
+ else
{
- case ExifTagValue.DocumentName:
- case ExifTagValue.ImageDescription:
- case ExifTagValue.Make:
- case ExifTagValue.Model:
- case ExifTagValue.Software:
- case ExifTagValue.DateTime:
- case ExifTagValue.Artist:
- case ExifTagValue.HostComputer:
- case ExifTagValue.TargetPrinter:
- case ExifTagValue.XMP:
- case ExifTagValue.Rating:
- case ExifTagValue.RatingPercent:
- case ExifTagValue.ImageID:
- case ExifTagValue.Copyright:
- case ExifTagValue.MDLabName:
- case ExifTagValue.MDSampleInfo:
- case ExifTagValue.MDPrepDate:
- case ExifTagValue.MDPrepTime:
- case ExifTagValue.MDFileUnits:
- case ExifTagValue.SEMInfo:
- case ExifTagValue.XPTitle:
- case ExifTagValue.XPComment:
- case ExifTagValue.XPAuthor:
- case ExifTagValue.XPKeywords:
- case ExifTagValue.XPSubject:
- return true;
- default:
- return false;
+ tiffFrameMetadata.Remove(ExifTag.IccProfile);
+ }
+
+ TiffMetadata tiffMetadata = imageMetadata.GetTiffMetadata();
+ if (tiffMetadata.XmpProfile != null)
+ {
+ var xmp = new ExifByteArray(ExifTagValue.XMP, ExifDataType.Byte)
+ {
+ Value = tiffMetadata.XmpProfile
+ };
+
+ this.collector.AddInternal(xmp);
+ }
+ else
+ {
+ tiffFrameMetadata.Remove(ExifTag.XMP);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
index 36fd3ad20..0e698c568 100644
--- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
@@ -2,9 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
-using System.Linq;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
@@ -14,7 +15,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public class TiffFrameMetadata : IDeepCloneable
{
- private const TiffResolutionUnit DefaultResolutionUnit = TiffResolutionUnit.Inch;
+ // 2 (Inch)
+ private const ushort DefaultResolutionUnit = 2;
private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky;
@@ -28,9 +30,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
}
///
- /// Gets or sets the Tiff directory tags list.
+ /// Gets the Tiff directory tags list.
///
- public List FrameTags { get; set; } = new List();
+ public List FrameTags { get; internal set; } = new List();
/// Gets a general indication of the kind of data contained in this subfile.
/// A general indication of the kind of data contained in this subfile.
@@ -53,7 +55,41 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
/// Gets the number of bits per component.
///
- public ushort[] BitsPerSample => this.GetArray(ExifTag.BitsPerSample, true);
+ public ushort[] BitsPerSample
+ {
+ get
+ {
+ var bits = this.GetArray(ExifTag.BitsPerSample, true);
+ if (bits == null)
+ {
+ if (this.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero
+ || this.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)
+ {
+ bits = new[] { (ushort)1 };
+ }
+ else
+ {
+ TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing.");
+ }
+ }
+
+ return bits;
+ }
+ }
+
+ internal int BitsPerPixel
+ {
+ get
+ {
+ int bitsPerPixel = 0;
+ foreach (var bits in this.BitsPerSample)
+ {
+ bitsPerPixel += bits;
+ }
+
+ return bitsPerPixel;
+ }
+ }
/// Gets the compression scheme used on the image data.
/// The compression scheme used on the image data.
@@ -116,61 +152,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// Gets the resolution of the image in x- direction.
/// The density of the image in x- direction.
- public double? HorizontalResolution
- {
- get
- {
- if (this.TryGetSingle(ExifTag.XResolution, out Rational xResolution))
- {
- return xResolution.ToDouble() * this.ResolutionFactor;
- }
-
- return null;
- }
-
- internal set
- {
- if (value == null)
- {
- this.Remove(ExifTag.XResolution);
- }
- else
- {
- double tag = value.Value / this.ResolutionFactor;
- this.SetSingle(ExifTag.XResolution, new Rational(tag));
- }
- }
- }
+ public double? HorizontalResolution => this.GetResolution(ExifTag.XResolution);
///
/// Gets the resolution of the image in y- direction.
///
/// The density of the image in y- direction.
- public double? VerticalResolution
- {
- get
- {
- if (this.TryGetSingle(ExifTag.YResolution, out Rational yResolution))
- {
- return yResolution.ToDouble() * this.ResolutionFactor;
- }
-
- return null;
- }
-
- internal set
- {
- if (value == null)
- {
- this.Remove(ExifTag.YResolution);
- }
- else
- {
- double tag = value.Value / this.ResolutionFactor;
- this.SetSingle(ExifTag.YResolution, new Rational(tag));
- }
- }
- }
+ public double? VerticalResolution => this.GetResolution(ExifTag.YResolution);
///
/// Gets how the components of each pixel are stored.
@@ -180,10 +168,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
/// Gets the unit of measurement for XResolution and YResolution.
///
- public TiffResolutionUnit ResolutionUnit
+ public PixelResolutionUnit ResolutionUnit
{
- get => this.GetSingleEnum(ExifTag.ResolutionUnit, DefaultResolutionUnit);
- internal set => this.SetSingleEnum(ExifTag.ResolutionUnit, value);
+ get
+ {
+ if (!this.TryGetSingle(ExifTag.ResolutionUnit, out ushort res))
+ {
+ res = DefaultResolutionUnit;
+ }
+
+ return (PixelResolutionUnit)(res - 1);
+ }
}
///
@@ -252,23 +247,34 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public TiffSampleFormat[] SampleFormat => this.GetEnumArray(ExifTag.SampleFormat, true);
- private double ResolutionFactor
+
+ ///
+ /// Clears the metadata.
+ ///
+ public void ClearMetadata()
{
- get
+ var tags = new List();
+ foreach (IExifValue entry in this.FrameTags)
{
- TiffResolutionUnit unit = this.ResolutionUnit;
- if (unit == TiffResolutionUnit.Centimeter)
- {
- return 2.54;
- }
- else if (unit == TiffResolutionUnit.Inch)
+ switch ((ExifTagValue)(ushort)entry.Tag)
{
- return 1.0;
+ case ExifTagValue.ImageWidth:
+ case ExifTagValue.ImageLength:
+ case ExifTagValue.ResolutionUnit:
+ case ExifTagValue.XResolution:
+ case ExifTagValue.YResolution:
+ //// image format tags
+ case ExifTagValue.Predictor:
+ case ExifTagValue.PlanarConfiguration:
+ case ExifTagValue.PhotometricInterpretation:
+ case ExifTagValue.BitsPerSample:
+ case ExifTagValue.ColorMap:
+ tags.Add(entry);
+ break;
}
-
- // DefaultResolutionUnit is Inch
- return 1.0;
}
+
+ this.FrameTags = tags;
}
///
@@ -282,5 +288,84 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
return new TiffFrameMetadata() { FrameTags = tags };
}
+
+ internal void SetResolutions(PixelResolutionUnit unit, double horizontal, double vertical)
+ {
+ switch (unit)
+ {
+ case PixelResolutionUnit.AspectRatio:
+ case PixelResolutionUnit.PixelsPerInch:
+ case PixelResolutionUnit.PixelsPerCentimeter:
+ break;
+ case PixelResolutionUnit.PixelsPerMeter:
+ {
+ unit = PixelResolutionUnit.PixelsPerCentimeter;
+ horizontal = UnitConverter.MeterToCm(horizontal);
+ vertical = UnitConverter.MeterToCm(vertical);
+ }
+
+ break;
+ default:
+ unit = PixelResolutionUnit.PixelsPerInch;
+ break;
+ }
+
+ this.SetSingle(ExifTag.ResolutionUnit, (ushort)unit + 1);
+ this.SetResolution(ExifTag.XResolution, horizontal);
+ this.SetResolution(ExifTag.YResolution, vertical);
+ }
+
+ private double? GetResolution(ExifTag tag)
+ {
+ if (!this.TryGetSingle(tag, out Rational resolution))
+ {
+ return null;
+ }
+
+ double res = resolution.ToDouble();
+ switch (this.ResolutionUnit)
+ {
+ case PixelResolutionUnit.AspectRatio:
+ return 0;
+ case PixelResolutionUnit.PixelsPerCentimeter:
+ return UnitConverter.CmToInch(res);
+ case PixelResolutionUnit.PixelsPerMeter:
+ return UnitConverter.MeterToInch(res);
+ case PixelResolutionUnit.PixelsPerInch:
+ default:
+ // DefaultResolutionUnit is Inch
+ return res;
+ }
+ }
+
+ private void SetResolution(ExifTag tag, double? value)
+ {
+ if (value == null)
+ {
+ this.Remove(tag);
+ return;
+ }
+ else
+ {
+ double res = value.Value;
+ switch (this.ResolutionUnit)
+ {
+ case PixelResolutionUnit.AspectRatio:
+ res = 0;
+ break;
+ case PixelResolutionUnit.PixelsPerCentimeter:
+ res = UnitConverter.InchToCm(res);
+ break;
+ case PixelResolutionUnit.PixelsPerMeter:
+ res = UnitConverter.InchToMeter(res);
+ break;
+ case PixelResolutionUnit.PixelsPerInch:
+ default:
+ break;
+ }
+
+ this.SetSingle(tag, new Rational(res));
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadataExtensions.cs
index 263b8cd55..39d0f34db 100644
--- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadataExtensions.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using System.Linq;
-
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
@@ -52,8 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
if (meta.TryGetArray(tag, out TTagValue[] result))
{
- // todo: improve
- return result.Select(a => (TEnum)(object)a).ToArray();
+ return System.Array.ConvertAll(result, a => (TEnum)(object)a);
}
if (!optional)
diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs
index 6f31fd549..f8df21c1e 100644
--- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs
@@ -26,22 +26,23 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.ByteOrder = other.ByteOrder;
this.XmpProfile = other.XmpProfile;
this.BitsPerPixel = other.BitsPerPixel;
+ this.Compression = other.Compression;
}
///
- /// Gets or sets the byte order.
+ /// Gets the byte order.
///
- public ByteOrder ByteOrder { get; set; }
+ public ByteOrder ByteOrder { get; internal set; }
///
- /// Gets or sets the number of bits per pixel.
+ /// Gets the number of bits per pixel.
///
- public TiffBitsPerPixel BitsPerPixel { get; set; } = TiffBitsPerPixel.Pixel24;
+ public TiffBitsPerPixel BitsPerPixel { get; internal set; } = TiffBitsPerPixel.Pixel24;
///
- /// Gets or sets the compression used to create the TIFF file.
+ /// Gets the compression used to create the TIFF file.
///
- public TiffCompression Compression { get; set; } = TiffCompression.None;
+ public TiffCompression Compression { get; internal set; } = TiffCompression.None;
///
/// Gets or sets the XMP profile.
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
index ce20fb77d..be14f7019 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
@@ -1,12 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -157,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(10, frame.HorizontalResolution);
Assert.Equal(10, frame.VerticalResolution);
Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration);
- Assert.Equal(TiffResolutionUnit.Inch, frame.ResolutionUnit);
+ Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit);
Assert.Equal("IrfanView", frame.Software);
Assert.Null(frame.DateTime);
Assert.Equal("This is author1;Author2", frame.Artist);
@@ -204,22 +207,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(SampleMetadata, PixelTypes.Rgba32, true)]
[WithFile(SampleMetadata, PixelTypes.Rgba32, false)]
- public void Tiff_PreserveMetadata(TestImageProvider provider, bool preserveMetadata)
+ public void PreserveMetadata(TestImageProvider provider, bool preserveMetadata)
where TPixel : unmanaged, IPixel
{
+ // Load Tiff image
using Image image = provider.GetImage(new TiffDecoder() { IgnoreMetadata = false });
ImageMetadata coreMeta = image.Metadata;
TiffMetadata tiffMeta = image.Metadata.GetTiffMetadata();
TiffFrameMetadata frameMeta = image.Frames.RootFrame.Metadata.GetTiffMetadata();
- var tiffEncoder = new TiffEncoder() { PreserveMetadata = preserveMetadata };
- using var ms = new MemoryStream();
+ // Save to Tiff
+ var tiffEncoder = new TiffEncoder() { Mode = TiffEncodingMode.Rgb };
+ if (!preserveMetadata)
+ {
+ ClearMeta(image);
+ }
- // act
+ using var ms = new MemoryStream();
image.Save(ms, tiffEncoder);
- // assert
+ // Assert
ms.Position = 0;
using var output = Image.Load(ms);
@@ -227,6 +235,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata();
TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata();
+ Assert.Equal(TiffBitsPerPixel.Pixel4, tiffMeta.BitsPerPixel);
+ Assert.Equal(TiffBitsPerPixel.Pixel24, tiffMetaOut.BitsPerPixel);
+ Assert.Equal(TiffCompression.Lzw, tiffMeta.Compression);
+ Assert.Equal(TiffCompression.None, tiffMetaOut.Compression);
+
Assert.Equal(coreMeta.HorizontalResolution, coreMetaOut.HorizontalResolution);
Assert.Equal(coreMeta.VerticalResolution, coreMetaOut.VerticalResolution);
Assert.Equal(coreMeta.ResolutionUnits, coreMetaOut.ResolutionUnits);
@@ -237,14 +250,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution);
Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution);
- Assert.Equal("IrfanView", frameMeta.Software);
- Assert.Equal("This is Название", frameMeta.ImageDescription);
- Assert.Equal("This is Изготовитель камеры", frameMeta.Make);
- Assert.Equal("This is Авторские права", frameMeta.Copyright);
+ Assert.Equal("ImageSharp", frameMetaOut.Software);
if (preserveMetadata)
{
- Assert.Equal("ImageSharp", frameMetaOut.Software);
+ Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile);
+
+ Assert.Equal("IrfanView", frameMeta.Software);
+ Assert.Equal("This is Название", frameMeta.ImageDescription);
+ Assert.Equal("This is Изготовитель камеры", frameMeta.Make);
+ Assert.Equal("This is Авторские права", frameMeta.Copyright);
Assert.Equal(frameMeta.ImageDescription, frameMetaOut.ImageDescription);
Assert.Equal(frameMeta.Make, frameMetaOut.Make);
@@ -252,7 +267,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
}
else
{
- Assert.Equal("ImageSharp", frameMetaOut.Software);
+ Assert.Null(tiffMetaOut.XmpProfile);
+
+ Assert.Null(frameMeta.Software);
+ Assert.Null(frameMeta.ImageDescription);
+ Assert.Null(frameMeta.Make);
+ Assert.Null(frameMeta.Copyright);
Assert.Null(frameMetaOut.ImageDescription);
Assert.Null(frameMetaOut.Make);
@@ -263,28 +283,50 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[InlineData(true)]
[InlineData(false)]
- public void Tiff_CreateMetadata(bool preserveMetadata)
+ public void CreateMetadata(bool preserveMetadata)
{
- var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffEncoderCompression.Deflate, PreserveMetadata = preserveMetadata };
-
+ // Create image
int w = 10;
int h = 20;
- using Image input = new Image(w, h);
- ImageMetadata coreMeta = input.Metadata;
- TiffMetadata tiffMeta = input.Metadata.GetTiffMetadata();
- TiffFrameMetadata frameMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata();
+ 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.PixelsPerInch;
- coreMeta.HorizontalResolution = 45;
- coreMeta.VerticalResolution = 54;
+ coreMeta.ResolutionUnits = PixelResolutionUnit.PixelsPerMeter;
+ coreMeta.HorizontalResolution = 4500;
+ coreMeta.VerticalResolution = 5400;
+ var datetime = System.DateTime.Now.ToString();
frameMeta.ImageDescription = "test ImageDescription";
- frameMeta.DateTime = System.DateTime.Now.ToString();
+ frameMeta.DateTime = datetime;
+
+ // Save to Tiff
+ var tiffEncoder = new TiffEncoder { Mode = TiffEncodingMode.Default, Compression = TiffEncoderCompression.Deflate };
+ if (!preserveMetadata)
+ {
+ ClearMeta(image);
+ }
using var ms = new MemoryStream();
- input.Save(ms, tiffEncoder);
+ image.Save(ms, tiffEncoder);
- // assert
+ // Assert
ms.Position = 0;
using var output = Image.Load(ms);
TiffMetadata meta = output.Metadata.GetTiffMetadata();
@@ -293,9 +335,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
TiffMetadata tiffMetaOut = output.Metadata.GetTiffMetadata();
TiffFrameMetadata frameMetaOut = output.Frames.RootFrame.Metadata.GetTiffMetadata();
- Assert.Equal(coreMeta.HorizontalResolution, coreMetaOut.HorizontalResolution);
- Assert.Equal(coreMeta.VerticalResolution, coreMetaOut.VerticalResolution);
- Assert.Equal(coreMeta.ResolutionUnits, coreMetaOut.ResolutionUnits);
+ 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.Pixel24, tiffMetaOut.BitsPerPixel);
Assert.Equal((uint)w, frameMetaOut.Width);
Assert.Equal((uint)h, frameMetaOut.Height);
@@ -303,16 +348,52 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(frameMeta.HorizontalResolution, frameMetaOut.HorizontalResolution);
Assert.Equal(frameMeta.VerticalResolution, frameMetaOut.VerticalResolution);
+ Assert.Equal("ImageSharp", frameMetaOut.Software);
+
if (preserveMetadata)
{
+ Assert.NotNull(tiffMeta.XmpProfile);
+ Assert.NotNull(coreMeta.IptcProfile);
+ Assert.NotNull(coreMeta.IccProfile);
+
+ Assert.Equal(tiffMeta.XmpProfile, tiffMetaOut.XmpProfile);
+ //// todo: failure Assert.Equal(coreMeta.IptcProfile, coreMetaOut.IptcProfile);
+ Assert.Equal(coreMeta.IptcProfile.Data, coreMetaOut.IptcProfile.Data);
+ Assert.Equal(coreMeta.IccProfile.ToByteArray(), coreMetaOut.IccProfile.ToByteArray());
+
+ Assert.Equal("test ImageDescription", frameMeta.ImageDescription);
+ Assert.Equal(datetime, frameMeta.DateTime);
+
Assert.Equal(frameMeta.ImageDescription, frameMetaOut.ImageDescription);
Assert.Equal(frameMeta.DateTime, frameMetaOut.DateTime);
}
else
{
+ Assert.Null(tiffMetaOut.XmpProfile);
+ Assert.Null(coreMetaOut.IptcProfile);
+ Assert.Null(coreMetaOut.IccProfile);
+
+ Assert.Null(frameMeta.ImageDescription);
+ Assert.Null(frameMeta.DateTime);
+
Assert.Null(frameMetaOut.ImageDescription);
Assert.Null(frameMetaOut.DateTime);
}
}
+
+ 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;
+
+ tiffMeta.XmpProfile = null;
+
+ frameMeta.ClearMetadata();
+ }
}
}