Browse Source

Write metadata to a TIFF file

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
596a738daa
  1. 98
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  2. 354
      src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs
  3. 58
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs
  4. 410
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs
  5. 77
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs

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

@ -39,6 +39,16 @@ namespace ImageSharp.Formats
this.options = options ?? new TiffEncoderOptions();
}
/// <summary>
/// Gets or sets the photometric interpretation implementation to use when encoding the image.
/// </summary>
public TiffColorType ColorType { get; set; }
/// <summary>
/// Gets or sets the compression implementation to use when encoding the image.
/// </summary>
public TiffCompressionType CompressionType { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
@ -138,6 +148,94 @@ namespace ImageSharp.Formats
/// <returns>The marker to write the next IFD offset (if present).</returns>
public long WriteImage<TPixel>(TiffWriter writer, Image<TPixel> image, long ifdOffset)
where TPixel : struct, IPixel<TPixel>
{
List<TiffIfdEntry> ifdEntries = new List<TiffIfdEntry>();
this.AddImageFormat(image, ifdEntries);
this.AddMetadata(image, ifdEntries);
writer.WriteMarker(ifdOffset, (uint)writer.Position);
long nextIfdMarker = this.WriteIfd(writer, ifdEntries);
return nextIfdMarker;
}
/// <summary>
/// Adds image metadata to the specified IFD.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="ifdEntries">The metadata entries to add to the IFD.</param>
public void AddMetadata<TPixel>(Image<TPixel> image, List<TiffIfdEntry> ifdEntries)
where TPixel : struct, IPixel<TPixel>
{
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;
}
}
}
}
/// <summary>
/// Adds image format information to the specified IFD.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="ifdEntries">The image format entries to add to the IFD.</param>
public void AddImageFormat<TPixel>(Image<TPixel> image, List<TiffIfdEntry> ifdEntries)
where TPixel : struct, IPixel<TPixel>
{
throw new NotImplementedException();
}

354
src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfdEntryCreator.cs

@ -0,0 +1,354 @@
// <copyright file="TiffIfdEntryCreator.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Tiff
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Utility class for generating TIFF IFD entries.
/// </summary>
internal static class TiffIfdEntryCreator
{
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Byte' from a unsigned integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedByte(this List<TiffIfdEntry> entries, ushort tag, uint value)
{
TiffIfdEntryCreator.AddUnsignedByte(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Byte' from an array of unsigned integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedByte(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Short' from a unsigned integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedShort(this List<TiffIfdEntry> entries, ushort tag, uint value)
{
TiffIfdEntryCreator.AddUnsignedShort(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Short' from an array of unsigned integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedShort(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Long' from a unsigned integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedLong(this List<TiffIfdEntry> entries, ushort tag, uint value)
{
TiffIfdEntryCreator.AddUnsignedLong(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Long' from an array of unsigned integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedLong(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SByte' from a signed integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedByte(this List<TiffIfdEntry> entries, ushort tag, int value)
{
TiffIfdEntryCreator.AddSignedByte(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SByte' from an array of signed integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedByte(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SShort' from a signed integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedShort(this List<TiffIfdEntry> entries, ushort tag, int value)
{
TiffIfdEntryCreator.AddSignedShort(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SShort' from an array of signed integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedShort(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SLong' from a signed integer.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedLong(this List<TiffIfdEntry> entries, ushort tag, int value)
{
TiffIfdEntryCreator.AddSignedLong(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SLong' from an array of signed integers.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedLong(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Ascii' from a string.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddAscii(this List<TiffIfdEntry> entries, ushort tag, string value)
{
byte[] bytes = Encoding.UTF8.GetBytes(value + "\0");
entries.Add(new TiffIfdEntry(tag, TiffType.Ascii, (uint)bytes.Length, bytes));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Rational' from a <see cref="Rational"/>.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedRational(this List<TiffIfdEntry> entries, ushort tag, Rational value)
{
TiffIfdEntryCreator.AddUnsignedRational(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Rational' from an array of <see cref="Rational"/> values.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddUnsignedRational(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SRational' from a <see cref="SignedRational"/>.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedRational(this List<TiffIfdEntry> entries, ushort tag, SignedRational value)
{
TiffIfdEntryCreator.AddSignedRational(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'SRational' from an array of <see cref="SignedRational"/> values.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddSignedRational(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Float' from a floating-point value.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddFloat(this List<TiffIfdEntry> entries, ushort tag, float value)
{
TiffIfdEntryCreator.AddFloat(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Float' from an array of floating-point values.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddFloat(this List<TiffIfdEntry> 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));
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Double' from a floating-point value.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddDouble(this List<TiffIfdEntry> entries, ushort tag, double value)
{
TiffIfdEntryCreator.AddDouble(entries, tag, new[] { value });
}
/// <summary>
/// Adds a new <see cref="TiffIfdEntry"/> of type 'Double' from an array of floating-point values.
/// </summary>
/// <param name="entries">The list of <see cref="TiffIfdEntry"/> to add the new entry to.</param>
/// <param name="tag">The tag for the resulting entry.</param>
/// <param name="value">The value for the resulting entry.</param>
public static void AddDouble(this List<TiffIfdEntry> 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);
}
}
}

58
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderMetadataTests.cs

@ -0,0 +1,58 @@
// <copyright file="TiffEncoderMetadataTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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<Rgba32> image = new Image<Rgba32>(100, 100);
image.MetaData.HorizontalResolution = 40.0;
image.MetaData.VerticalResolution = 50.5;
TiffEncoderCore encoder = new TiffEncoderCore(null);
List<TiffIfdEntry> ifdEntries = new List<TiffIfdEntry>();
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<Rgba32> image = new Image<Rgba32>(100, 100);
image.MetaData.Properties.Add(new ImageProperty(metadataName, metadataValue));
TiffEncoderCore encoder = new TiffEncoderCore(null);
List<TiffIfdEntry> ifdEntries = new List<TiffIfdEntry>();
encoder.AddMetadata(image, ifdEntries);
Assert.Equal(metadataValue + "\0", ifdEntries.GetAscii(tag));
}
}
}

410
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryCreatorTests.cs

@ -0,0 +1,410 @@
// <copyright file="TiffIfdEntryCreatorTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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<TiffIfdEntry>();
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);
}
}
}

77
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffIfdParser.cs

@ -0,0 +1,77 @@
// <copyright file="TiffIfdParser.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageSharp.Formats.Tiff;
using Xunit;
/// <summary>
/// A utility data structure to decode Tiff IFD entries in unit tests.
/// </summary>
internal static class TiffIfdParser
{
public static int? GetInteger(this List<TiffIfdEntry> 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<TiffIfdEntry> 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<TiffIfdEntry> 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);
}
}
}
Loading…
Cancel
Save