diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index d32e34c433..5f1148adea 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -39,6 +39,16 @@ namespace ImageSharp.Formats
this.options = options ?? new TiffEncoderOptions();
}
+ ///
+ /// Gets or sets the photometric interpretation implementation to use when encoding the image.
+ ///
+ public TiffColorType ColorType { get; set; }
+
+ ///
+ /// Gets or sets the compression implementation to use when encoding the image.
+ ///
+ public TiffCompressionType CompressionType { get; set; }
+
///
/// Encodes the image to the specified stream from the .
///
@@ -138,6 +148,94 @@ namespace ImageSharp.Formats
/// The marker to write the next IFD offset (if present).
public long WriteImage(TiffWriter writer, Image image, long ifdOffset)
where TPixel : struct, IPixel
+ {
+ List ifdEntries = new List();
+
+ this.AddImageFormat(image, ifdEntries);
+ this.AddMetadata(image, ifdEntries);
+
+ writer.WriteMarker(ifdOffset, (uint)writer.Position);
+ long nextIfdMarker = this.WriteIfd(writer, ifdEntries);
+
+ return nextIfdMarker;
+ }
+
+ ///
+ /// Adds image metadata to the specified IFD.
+ ///
+ /// The pixel format.
+ /// The to encode from.
+ /// The metadata entries to add to the IFD.
+ public void AddMetadata(Image image, List ifdEntries)
+ where TPixel : struct, IPixel
+ {
+ ifdEntries.AddUnsignedRational(TiffTags.XResolution, new Rational(image.MetaData.HorizontalResolution));
+ ifdEntries.AddUnsignedRational(TiffTags.YResolution, new Rational(image.MetaData.VerticalResolution));
+ ifdEntries.AddUnsignedShort(TiffTags.ResolutionUnit, (uint)TiffResolutionUnit.Inch);
+
+ foreach (ImageProperty metadata in image.MetaData.Properties)
+ {
+ switch (metadata.Name)
+ {
+ case TiffMetadataNames.Artist:
+ {
+ ifdEntries.AddAscii(TiffTags.Artist, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.Copyright:
+ {
+ ifdEntries.AddAscii(TiffTags.Copyright, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.DateTime:
+ {
+ ifdEntries.AddAscii(TiffTags.DateTime, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.HostComputer:
+ {
+ ifdEntries.AddAscii(TiffTags.HostComputer, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.ImageDescription:
+ {
+ ifdEntries.AddAscii(TiffTags.ImageDescription, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.Make:
+ {
+ ifdEntries.AddAscii(TiffTags.Make, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.Model:
+ {
+ ifdEntries.AddAscii(TiffTags.Model, metadata.Value);
+ break;
+ }
+
+ case TiffMetadataNames.Software:
+ {
+ ifdEntries.AddAscii(TiffTags.Software, metadata.Value);
+ break;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Adds image format information to the specified IFD.
+ ///
+ /// The pixel format.
+ /// The to encode from.
+ /// The image format entries to add to the IFD.
+ public void AddImageFormat(Image image, List ifdEntries)
+ where TPixel : struct, IPixel
{
throw new NotImplementedException();
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs
new file mode 100644
index 0000000000..c30ce5c8a4
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs
@@ -0,0 +1,354 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Tiff
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ ///
+ /// Utility class for generating TIFF IFD entries.
+ ///
+ internal static class TiffIfdEntryCreator
+ {
+ ///
+ /// Adds a new of type 'Byte' from a unsigned integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedByte(this List entries, ushort tag, uint value)
+ {
+ TiffIfdEntryCreator.AddUnsignedByte(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Byte' from an array of unsigned integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedByte(this List entries, ushort tag, uint[] value)
+ {
+ byte[] bytes = new byte[value.Length];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ bytes[i] = (byte)value[i];
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Byte, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Short' from a unsigned integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedShort(this List entries, ushort tag, uint value)
+ {
+ TiffIfdEntryCreator.AddUnsignedShort(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Short' from an array of unsigned integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedShort(this List entries, ushort tag, uint[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfShort];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ ToBytes((ushort)value[i], bytes, i * TiffConstants.SizeOfShort);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Short, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Long' from a unsigned integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedLong(this List entries, ushort tag, uint value)
+ {
+ TiffIfdEntryCreator.AddUnsignedLong(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Long' from an array of unsigned integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedLong(this List entries, ushort tag, uint[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfLong];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ ToBytes(value[i], bytes, i * TiffConstants.SizeOfLong);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Long, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'SByte' from a signed integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedByte(this List entries, ushort tag, int value)
+ {
+ TiffIfdEntryCreator.AddSignedByte(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'SByte' from an array of signed integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedByte(this List entries, ushort tag, int[] value)
+ {
+ byte[] bytes = new byte[value.Length];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ bytes[i] = (byte)((sbyte)value[i]);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.SByte, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'SShort' from a signed integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedShort(this List entries, ushort tag, int value)
+ {
+ TiffIfdEntryCreator.AddSignedShort(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'SShort' from an array of signed integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedShort(this List entries, ushort tag, int[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfShort];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ ToBytes((short)value[i], bytes, i * TiffConstants.SizeOfShort);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.SShort, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'SLong' from a signed integer.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedLong(this List entries, ushort tag, int value)
+ {
+ TiffIfdEntryCreator.AddSignedLong(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'SLong' from an array of signed integers.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedLong(this List entries, ushort tag, int[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfLong];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ ToBytes(value[i], bytes, i * TiffConstants.SizeOfLong);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.SLong, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Ascii' from a string.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddAscii(this List entries, ushort tag, string value)
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(value + "\0");
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Ascii, (uint)bytes.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Rational' from a .
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedRational(this List entries, ushort tag, Rational value)
+ {
+ TiffIfdEntryCreator.AddUnsignedRational(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Rational' from an array of values.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddUnsignedRational(this List entries, ushort tag, Rational[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfRational];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ int offset = i * TiffConstants.SizeOfRational;
+ ToBytes(value[i].Numerator, bytes, offset);
+ ToBytes(value[i].Denominator, bytes, offset + TiffConstants.SizeOfLong);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Rational, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'SRational' from a .
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedRational(this List entries, ushort tag, SignedRational value)
+ {
+ TiffIfdEntryCreator.AddSignedRational(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'SRational' from an array of values.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddSignedRational(this List entries, ushort tag, SignedRational[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfRational];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ int offset = i * TiffConstants.SizeOfRational;
+ ToBytes(value[i].Numerator, bytes, offset);
+ ToBytes(value[i].Denominator, bytes, offset + TiffConstants.SizeOfLong);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.SRational, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Float' from a floating-point value.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddFloat(this List entries, ushort tag, float value)
+ {
+ TiffIfdEntryCreator.AddFloat(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Float' from an array of floating-point values.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddFloat(this List entries, ushort tag, float[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfFloat];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ byte[] itemBytes = BitConverter.GetBytes(value[i]);
+ Array.Copy(itemBytes, 0, bytes, i * TiffConstants.SizeOfFloat, TiffConstants.SizeOfFloat);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Float, (uint)value.Length, bytes));
+ }
+
+ ///
+ /// Adds a new of type 'Double' from a floating-point value.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddDouble(this List entries, ushort tag, double value)
+ {
+ TiffIfdEntryCreator.AddDouble(entries, tag, new[] { value });
+ }
+
+ ///
+ /// Adds a new of type 'Double' from an array of floating-point values.
+ ///
+ /// The list of to add the new entry to.
+ /// The tag for the resulting entry.
+ /// The value for the resulting entry.
+ public static void AddDouble(this List entries, ushort tag, double[] value)
+ {
+ byte[] bytes = new byte[value.Length * TiffConstants.SizeOfDouble];
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ byte[] itemBytes = BitConverter.GetBytes(value[i]);
+ Array.Copy(itemBytes, 0, bytes, i * TiffConstants.SizeOfDouble, TiffConstants.SizeOfDouble);
+ }
+
+ entries.Add(new TiffIfdEntry(tag, TiffType.Double, (uint)value.Length, bytes));
+ }
+
+ private static void ToBytes(ushort value, byte[] bytes, int offset)
+ {
+ bytes[offset + 0] = (byte)value;
+ bytes[offset + 1] = (byte)(value >> 8);
+ }
+
+ private static void ToBytes(uint value, byte[] bytes, int offset)
+ {
+ bytes[offset + 0] = (byte)value;
+ bytes[offset + 1] = (byte)(value >> 8);
+ bytes[offset + 2] = (byte)(value >> 16);
+ bytes[offset + 3] = (byte)(value >> 24);
+ }
+
+ private static void ToBytes(short value, byte[] bytes, int offset)
+ {
+ bytes[offset + 0] = (byte)value;
+ bytes[offset + 1] = (byte)(value >> 8);
+ }
+
+ private static void ToBytes(int value, byte[] bytes, int offset)
+ {
+ bytes[offset + 0] = (byte)value;
+ bytes[offset + 1] = (byte)(value >> 8);
+ bytes[offset + 2] = (byte)(value >> 16);
+ bytes[offset + 3] = (byte)(value >> 24);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs
new file mode 100644
index 0000000000..4dec7630cd
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs
@@ -0,0 +1,58 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.IO;
+ using System.Linq;
+ using Xunit;
+
+ using ImageSharp.Formats;
+ using ImageSharp.Formats.Tiff;
+ using System.Collections.Generic;
+
+ public class TiffEncoderMetadataTests
+ {
+ public static object[][] BaselineMetadataValues = new[] { new object[] { TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" },
+ new object[] { TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" },
+ new object[] { TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" },
+ new object[] { TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" },
+ new object[] { TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" },
+ new object[] { TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" },
+ new object[] { TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" },
+ new object[] { TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }};
+
+ [Fact]
+ public void AddMetadata_SetsImageResolution()
+ {
+ Image image = new Image(100, 100);
+ image.MetaData.HorizontalResolution = 40.0;
+ image.MetaData.VerticalResolution = 50.5;
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List ifdEntries = new List();
+ encoder.AddMetadata(image, ifdEntries);
+
+ Assert.Equal(new Rational(40, 1), ifdEntries.GetUnsignedRational(TiffTags.XResolution));
+ Assert.Equal(new Rational(101, 2), ifdEntries.GetUnsignedRational(TiffTags.YResolution));
+ Assert.Equal(TiffResolutionUnit.Inch, (TiffResolutionUnit?)ifdEntries.GetInteger(TiffTags.ResolutionUnit));
+ }
+
+ [Theory]
+ [MemberData(nameof(BaselineMetadataValues))]
+ public void AddMetadata_SetsAsciiMetadata(ushort tag, string metadataName, string metadataValue)
+ {
+ Image image = new Image(100, 100);
+ image.MetaData.Properties.Add(new ImageProperty(metadataName, metadataValue));
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List ifdEntries = new List();
+ encoder.AddMetadata(image, ifdEntries);
+
+ Assert.Equal(metadataValue + "\0", ifdEntries.GetAscii(tag));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs
new file mode 100644
index 0000000000..036ab46218
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs
@@ -0,0 +1,410 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using Xunit;
+
+ using ImageSharp.Formats;
+ using ImageSharp.Formats.Tiff;
+
+ public class TiffIfdEntryCreatorTests
+ {
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1 }, 1)]
+ [InlineDataAttribute(new byte[] { 255 }, 255)]
+ public void AddUnsignedByte_AddsSingleValue(byte[] bytes, uint value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedByte(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Byte, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0 }, new uint[] { 0 })]
+ [InlineDataAttribute(new byte[] { 0, 1, 2 }, new uint[] { 0, 1, 2 })]
+ [InlineDataAttribute(new byte[] { 0, 1, 2, 3, 4, 5, 6 }, new uint[] { 0, 1, 2, 3, 4, 5, 6 })]
+ public void AddUnsignedByte_AddsArray(byte[] bytes, uint[] value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedByte(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Byte, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1, 0 }, 1)]
+ [InlineDataAttribute(new byte[] { 0, 1 }, 256)]
+ [InlineDataAttribute(new byte[] { 2, 1 }, 258)]
+ [InlineDataAttribute(new byte[] { 255, 255 }, UInt16.MaxValue)]
+ public void AddUnsignedShort_AddsSingleValue(byte[] bytes, uint value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedShort(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Short, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 1, 0 }, new uint[] { 1 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 3, 2 }, new uint[] { 1, 515 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 3, 2, 5, 4 }, new uint[] { 1, 515, 1029 })]
+ public void AddUnsignedShort_AddsArray(byte[] bytes, uint[] value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedShort(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Short, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0 }, 1)]
+ [InlineDataAttribute(new byte[] { 0, 1, 0, 0 }, 256)]
+ [InlineDataAttribute(new byte[] { 0, 0, 1, 0 }, 256 * 256)]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)]
+ [InlineDataAttribute(new byte[] { 1, 2, 3, 4 }, 67305985)]
+ [InlineDataAttribute(new byte[] { 255, 255, 255, 255 }, UInt32.MaxValue)]
+ public void AddUnsignedLong_AddsSingleValue(byte[] bytes, uint value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedLong(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Long, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 4, 3, 2, 1 }, new uint[] { 0x01020304 })]
+ [InlineDataAttribute(new byte[] { 4, 3, 2, 1, 6, 5, 4, 3 }, new uint[] { 0x01020304, 0x03040506 })]
+ public void AddUnsignedLong_AddsArray(byte[] bytes, uint[] value)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedLong(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Long, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1 }, 1)]
+ [InlineDataAttribute(new byte[] { 255 }, -1)]
+ public void AddSignedByte_AddsSingleValue(byte[] bytes, int value)
+ {
+ var entries = new List();
+
+ entries.AddSignedByte(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SByte, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0 }, new int[] { 0 })]
+ [InlineDataAttribute(new byte[] { 0, 255, 2 }, new int[] { 0, -1, 2 })]
+ [InlineDataAttribute(new byte[] { 0, 255, 2, 3, 4, 5, 6 }, new int[] { 0, -1, 2, 3, 4, 5, 6 })]
+ public void AddSignedByte_AddsArray(byte[] bytes, int[] value)
+ {
+ var entries = new List();
+
+ entries.AddSignedByte(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SByte, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1, 0 }, 1)]
+ [InlineDataAttribute(new byte[] { 0, 1 }, 256)]
+ [InlineDataAttribute(new byte[] { 2, 1 }, 258)]
+ [InlineDataAttribute(new byte[] { 255, 255 }, -1)]
+ public void AddSignedShort_AddsSingleValue(byte[] bytes, int value)
+ {
+ var entries = new List();
+
+ entries.AddSignedShort(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SShort, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 1, 0 }, new int[] { 1 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 255, 255 }, new int[] { 1, -1 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 255, 255, 5, 4 }, new int[] { 1, -1, 1029 })]
+ public void AddSignedShort_AddsArray(byte[] bytes, int[] value)
+ {
+ var entries = new List();
+
+ entries.AddSignedShort(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SShort, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0 }, 0)]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0 }, 1)]
+ [InlineDataAttribute(new byte[] { 0, 1, 0, 0 }, 256)]
+ [InlineDataAttribute(new byte[] { 0, 0, 1, 0 }, 256 * 256)]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 1 }, 256 * 256 * 256)]
+ [InlineDataAttribute(new byte[] { 1, 2, 3, 4 }, 67305985)]
+ [InlineDataAttribute(new byte[] { 255, 255, 255, 255 }, -1)]
+ public void AddSignedLong_AddsSingleValue(byte[] bytes, int value)
+ {
+ var entries = new List();
+
+ entries.AddSignedLong(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SLong, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 4, 3, 2, 1 }, new int[] { 0x01020304 })]
+ [InlineDataAttribute(new byte[] { 4, 3, 2, 1, 255, 255, 255, 255 }, new int[] { 0x01020304, -1 })]
+ public void AddSignedLong_AddsArray(byte[] bytes, int[] value)
+ {
+ var entries = new List();
+
+ entries.AddSignedLong(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SLong, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0 }, "")]
+ [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 }, "ABC")]
+ [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', 0 }, "ABCDEF")]
+ [InlineDataAttribute(new byte[] { (byte)'A', (byte)'B', (byte)'C', (byte)'D', 0, (byte)'E', (byte)'F', (byte)'G', (byte)'H', 0 }, "ABCD\0EFGH")]
+ public void AddAscii_AddsEntry(byte[] bytes, string value)
+ {
+ var entries = new List();
+
+ entries.AddAscii(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Ascii, entry.Type);
+ Assert.Equal((uint)bytes.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0)]
+ [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, 2, 1)]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)]
+ public void AddUnsignedRational_AddsSingleValue(byte[] bytes, uint numerator, uint denominator)
+ {
+ var entries = new List();
+
+ entries.AddUnsignedRational(TiffTags.ImageWidth, new Rational(numerator, denominator));
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Rational, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new uint[] { 0 }, new uint[] { 0 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new uint[] { 1 }, new uint[] { 2 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new uint[] { 1, 2 }, new uint[] { 2, 3 })]
+ public void AddUnsignedRational_AddsArray(byte[] bytes, uint[] numerators, uint[] denominators)
+ {
+ var entries = new List();
+ Rational[] value = Enumerable.Range(0, numerators.Length).Select(i => new Rational(numerators[i], denominators[i])).ToArray();
+
+ entries.AddUnsignedRational(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Rational, entry.Type);
+ Assert.Equal((uint)numerators.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0)]
+ [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, 2, 1)]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, 1, 2)]
+ [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, -1, 2)]
+ public void AddSignedRational_AddsSingleValue(byte[] bytes, int numerator, int denominator)
+ {
+ var entries = new List();
+
+ entries.AddSignedRational(TiffTags.ImageWidth, new SignedRational(numerator, denominator));
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SRational, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 0 }, new int[] { 0 })]
+ [InlineDataAttribute(new byte[] { 2, 0, 0, 0, 1, 0, 0, 0 }, new int[] { 2 }, new int[] { 1 })]
+ [InlineDataAttribute(new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 }, new int[] { 1 }, new int[] { 2 })]
+ [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0 }, new int[] { -1 }, new int[] { 2 })]
+ [InlineDataAttribute(new byte[] { 255, 255, 255, 255, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 }, new int[] { -1, 2 }, new int[] { 2, 3 })]
+ public void AddSignedRational_AddsArray(byte[] bytes, int[] numerators, int[] denominators)
+ {
+ var entries = new List();
+ SignedRational[] value = Enumerable.Range(0, numerators.Length).Select(i => new SignedRational(numerators[i], denominators[i])).ToArray();
+
+ entries.AddSignedRational(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.SRational, entry.Type);
+ Assert.Equal((uint)numerators.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0.0F)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x3F }, 1.0F)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0xC0 }, -2.0F)]
+ [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, float.MaxValue)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x7F }, float.PositiveInfinity)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0xFF }, float.NegativeInfinity)]
+ public void AddFloat_AddsSingleValue(byte[] bytes, float value)
+ {
+ var entries = new List();
+
+ entries.AddFloat(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Float, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00 }, new float[] { 0.0F })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x3F }, new float[] { 1.0F })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0xC0 }, new float[] { -2.0F })]
+ [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0x7F, 0x7F }, new float[] { float.MaxValue })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0x7F }, new float[] { float.PositiveInfinity })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x80, 0xFF }, new float[] { float.NegativeInfinity })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xC0 }, new float[] { 0.0F, 1.0F, -2.0F })]
+ public void AddFloat_AddsArray(byte[] bytes, float[] value)
+ {
+ var entries = new List();
+
+ entries.AddFloat(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Float, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0.0)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, 1.0)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, 2.0)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, -2.0)]
+ [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, double.MaxValue)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, double.PositiveInfinity)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, double.NegativeInfinity)]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF }, double.NaN)]
+ public void AddDouble_AddsSingleValue(byte[] bytes, double value)
+ {
+ var entries = new List();
+
+ entries.AddDouble(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Double, entry.Type);
+ Assert.Equal(1u, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ [Theory]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new double[] { 0.0 })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F }, new double[] { 1.0 })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, new double[] { 2.0 })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { -2.0 })]
+ [InlineDataAttribute(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F }, new double[] { double.MaxValue })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F }, new double[] { double.PositiveInfinity })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF }, new double[] { double.NegativeInfinity })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF }, new double[] { double.NaN })]
+ [InlineDataAttribute(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 }, new double[] { 0.0, 1.0, -2.0 })]
+ public void AddDouble_AddsArray(byte[] bytes, double[] value)
+ {
+ var entries = new List();
+
+ entries.AddDouble(TiffTags.ImageWidth, value);
+
+ var entry = entries[0];
+ Assert.Equal(TiffTags.ImageWidth, entry.Tag);
+ Assert.Equal(TiffType.Double, entry.Type);
+ Assert.Equal((uint)value.Length, entry.Count);
+ Assert.Equal(bytes, entry.Value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs
new file mode 100644
index 0000000000..f8d7440374
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs
@@ -0,0 +1,77 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using ImageSharp.Formats.Tiff;
+ using Xunit;
+
+ ///
+ /// A utility data structure to decode Tiff IFD entries in unit tests.
+ ///
+ internal static class TiffIfdParser
+ {
+ public static int? GetInteger(this List entries, ushort tag)
+ {
+ TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag);
+
+ if (entry.Tag == 0)
+ return null;
+
+ Assert.Equal(1u, entry.Count);
+
+ switch (entry.Type)
+ {
+ case TiffType.Byte:
+ return entry.Value[0];
+ case TiffType.SByte:
+ return (sbyte)entry.Value[0];
+ case TiffType.Short:
+ return BitConverter.ToUInt16(entry.Value, 0);
+ case TiffType.SShort:
+ return BitConverter.ToInt16(entry.Value, 0);
+ case TiffType.Long:
+ return (int)BitConverter.ToUInt32(entry.Value, 0);
+ case TiffType.SLong:
+ return BitConverter.ToInt32(entry.Value, 0);
+ default:
+ Assert.True(1 == 1, "TIFF IFD entry is not convertable to an integer.");
+ return null;
+ }
+ }
+
+ public static Rational? GetUnsignedRational(this List entries, ushort tag)
+ {
+ TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag);
+
+ if (entry.Tag == 0)
+ return null;
+
+ Assert.Equal(TiffType.Rational, entry.Type);
+ Assert.Equal(1u, entry.Count);
+
+ uint numerator = BitConverter.ToUInt32(entry.Value, 0);
+ uint denominator = BitConverter.ToUInt32(entry.Value, 4);
+
+ return new Rational(numerator, denominator);
+ }
+
+ public static string GetAscii(this List entries, ushort tag)
+ {
+ TiffIfdEntry entry = entries.FirstOrDefault(e => e.Tag == tag);
+
+ if (entry.Tag == 0)
+ return null;
+
+ Assert.Equal(TiffType.Ascii, entry.Type);
+
+ return Encoding.UTF8.GetString(entry.Value, 0, (int)entry.Count);
+ }
+ }
+}
\ No newline at end of file