diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs
index 582c476443..307f9b9d23 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffResolutionUnit.cs
@@ -23,6 +23,6 @@ namespace ImageSharp.Formats
///
/// Centimeter.
///
- Centimeter = 2
+ Centimeter = 3
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index eb4c6b1c89..28d45a6e1f 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -170,6 +170,26 @@ namespace ImageSharp.Formats
int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry);
image.InitPixels(width, height);
+
+ TiffResolutionUnit resolutionUnit = TiffResolutionUnit.Inch;
+ if (ifd.TryGetIfdEntry(TiffTags.ResolutionUnit, out TiffIfdEntry resolutionUnitEntry))
+ {
+ resolutionUnit = (TiffResolutionUnit)this.ReadUnsignedInteger(ref resolutionUnitEntry);
+ }
+
+ double resolutionUnitFactor = resolutionUnit == TiffResolutionUnit.Centimeter ? 1.0 / 2.54 : 1.0;
+
+ if (ifd.TryGetIfdEntry(TiffTags.XResolution, out TiffIfdEntry xResolutionEntry))
+ {
+ Rational xResolution = this.ReadUnsignedRational(ref xResolutionEntry);
+ image.MetaData.HorizontalResolution = xResolution.ToDouble();
+ }
+
+ if (ifd.TryGetIfdEntry(TiffTags.YResolution, out TiffIfdEntry yResolutionEntry))
+ {
+ Rational yResolution = this.ReadUnsignedRational(ref yResolutionEntry);
+ image.MetaData.VerticalResolution = yResolution.ToDouble();
+ }
}
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs
index 2206e97f39..d2071aff9c 100644
--- a/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs
@@ -35,21 +35,31 @@ namespace ImageSharp.Formats
/// Gets the child with the specified tag ID.
///
/// The tag ID to search for.
- /// The resulting , if it exists.
- /// A flag indicating whether the requested entry exists
- public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry)
+ /// The resulting , or null if it does not exists.
+ public TiffIfdEntry? GetIfdEntry(ushort tag)
{
for (int i = 0; i < this.Entries.Length; i++)
{
if (this.Entries[i].Tag == tag)
{
- entry = this.Entries[i];
- return true;
+ return this.Entries[i];
}
}
- entry = default(TiffIfdEntry);
- return false;
+ return null;
+ }
+
+ ///
+ /// Gets the child with the specified tag ID.
+ ///
+ /// The tag ID to search for.
+ /// The resulting , if it exists.
+ /// A flag indicating whether the requested entry exists.
+ public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry)
+ {
+ TiffIfdEntry? nullableEntry = this.GetIfdEntry(tag);
+ entry = nullableEntry ?? default(TiffIfdEntry);
+ return nullableEntry.HasValue;
}
}
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs
index 824bbc3b5b..b0a97102fc 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs
@@ -14,6 +14,8 @@ namespace ImageSharp.Tests
{
public const int ImageWidth = 200;
public const int ImageHeight = 150;
+ public const int XResolution = 100;
+ public const int YResolution = 200;
public static object[][] IsLittleEndianValues = new[] { new object[] { false },
new object[] { true } };
@@ -27,14 +29,67 @@ namespace ImageSharp.Tests
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
- Image image = new Image(1,1);
+ Image image = new Image(1, 1);
decoder.DecodeImage(ifd, image);
-
+
Assert.Equal(ImageWidth, image.Width);
Assert.Equal(ImageHeight, image.Height);
}
+ [Theory]
+ [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)]
+ [InlineData(false, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)]
+ [InlineData(false, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)]
+ [InlineData(false, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)]
+ [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)]
+ [InlineData(false, null, null, null, null, null /* Inch */, 96.0, 96.0)]
+ [InlineData(false, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)]
+ [InlineData(false, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)]
+ [InlineData(true, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)]
+ [InlineData(true, 150u, 1u, 200u, 1u, 3u /* Cm */, 150.0 / 2.54, 200.0 / 2.54)]
+ [InlineData(true, 150u, 1u, 200u, 1u, 1u /* None */, 96.0, 96.0)]
+ [InlineData(false, 5u, 2u, 9u, 4u, 2u /* Inch */, 2.5, 2.25)]
+ [InlineData(true, 150u, 1u, 200u, 1u, null /* Inch */, 150.0, 200.0)]
+ [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)]
+ [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)]
+ [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)]
+ public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator,
+ uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit,
+ double expectedHorizonalResolution, double expectedVerticalResolution)
+ {
+ TiffGenIfd ifdGen = CreateTiffGenIfd()
+ .WithoutEntry(TiffTags.XResolution)
+ .WithoutEntry(TiffTags.YResolution)
+ .WithoutEntry(TiffTags.ResolutionUnit);
+
+ if (xResolutionNumerator != null)
+ {
+ ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.XResolution, xResolutionNumerator.Value, xResolutionDenominator.Value));
+ }
+
+ if (yResolutionNumerator != null)
+ {
+ ifdGen.WithEntry(TiffGenEntry.Rational(TiffTags.YResolution, yResolutionNumerator.Value, yResolutionDenominator.Value));
+ }
+
+ if (resolutionUnit != null)
+ {
+ ifdGen.WithEntry(TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, resolutionUnit.Value));
+ }
+
+ Stream stream = ifdGen.ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfd ifd = decoder.ReadIfd(0);
+ Image image = new Image(1, 1);
+
+ decoder.DecodeImage(ifd, image);
+
+ Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution);
+ Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution);
+ }
+
[Theory]
[MemberData(nameof(IsLittleEndianValues))]
public void DecodeImage_ThrowsException_WithMissingImageWidth(bool isLittleEndian)
@@ -45,8 +100,8 @@ namespace ImageSharp.Tests
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
- Image image = new Image(1,1);
-
+ Image image = new Image(1, 1);
+
var e = Assert.Throws(() => decoder.DecodeImage(ifd, image));
Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message);
@@ -62,8 +117,8 @@ namespace ImageSharp.Tests
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
- Image image = new Image(1,1);
-
+ Image image = new Image(1, 1);
+
var e = Assert.Throws(() => decoder.DecodeImage(ifd, image));
Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message);
@@ -72,13 +127,16 @@ namespace ImageSharp.Tests
private TiffGenIfd CreateTiffGenIfd()
{
return new TiffGenIfd()
- {
- Entries =
+ {
+ Entries =
{
TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, ImageWidth),
TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight),
+ TiffGenEntry.Rational(TiffTags.XResolution, XResolution, 1),
+ TiffGenEntry.Rational(TiffTags.YResolution, YResolution, 1),
+ TiffGenEntry.Integer(TiffTags.ResolutionUnit, TiffType.Short, 2)
}
- };
+ };
}
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs
index d9f8425cb6..c680475390 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs
@@ -22,6 +22,41 @@ namespace ImageSharp.Tests
Assert.Equal(1234u, ifd.NextIfdOffset);
}
+ [Fact]
+ public void GetIfdEntry_ReturnsIfdIfExists()
+ {
+ var entries = new[]
+ {
+ new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(40, TiffType.Short, 20, new byte[4])
+ };
+ var ifd = new TiffIfd(entries, 1234u);
+
+ TiffIfdEntry? entry = ifd.GetIfdEntry(30);
+
+ Assert.Equal(true, entry.HasValue);
+ Assert.Equal(30, entry.Value.Tag);
+ }
+
+ [Fact]
+ public void GetIfdEntry_ReturnsNullOtherwise()
+ {
+ var entries = new[]
+ {
+ new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]),
+ new TiffIfdEntry(40, TiffType.Short, 20, new byte[4])
+ };
+ var ifd = new TiffIfd(entries, 1234u);
+
+ TiffIfdEntry? entry = ifd.GetIfdEntry(25);
+
+ Assert.Equal(false, entry.HasValue);
+ }
+
[Fact]
public void TryGetIfdEntry_ReturnsIfdIfExists()
{
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
index c0bb9d78c8..2065e8501a 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
@@ -41,7 +41,7 @@ namespace ImageSharp.Tests
public static TiffGenEntry Integer(ushort tag, TiffType type, int value)
{
- return TiffGenEntry.Integer(tag, type, new int[] {value});
+ return TiffGenEntry.Integer(tag, type, new int[] { value });
}
public static TiffGenEntry Integer(ushort tag, TiffType type, int[] value)
@@ -55,7 +55,7 @@ namespace ImageSharp.Tests
public static TiffGenEntry Integer(ushort tag, TiffType type, uint value)
{
- return TiffGenEntry.Integer(tag, type, new uint[] {value});
+ return TiffGenEntry.Integer(tag, type, new uint[] { value });
}
public static TiffGenEntry Integer(ushort tag, TiffType type, uint[] value)
@@ -67,6 +67,11 @@ namespace ImageSharp.Tests
return new TiffGenEntryUnsignedInteger(tag, type, value);
}
+ public static TiffGenEntry Rational(ushort tag, uint numerator, uint denominator)
+ {
+ return new TiffGenEntryRational(tag, numerator, denominator);
+ }
+
private class TiffGenEntryAscii : TiffGenEntry
{
public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii, (uint)GetBytes(value).Length)
@@ -176,5 +181,26 @@ namespace ImageSharp.Tests
}
}
}
+
+ private class TiffGenEntryRational : TiffGenEntry
+ {
+ public TiffGenEntryRational(ushort tag, uint numerator, uint denominator) : base(tag, TiffType.Rational, 1u)
+ {
+ this.Numerator = numerator;
+ this.Denominator = denominator;
+ }
+
+ public uint Numerator { get; }
+
+ public uint Denominator { get; }
+
+ public override IEnumerable GetData(bool isLittleEndian)
+ {
+ byte[] numeratorBytes = BitConverter.GetBytes(Numerator).WithByteOrder(isLittleEndian);
+ byte[] denominatorBytes = BitConverter.GetBytes(Denominator).WithByteOrder(isLittleEndian);
+ byte[] bytes = Enumerable.Concat(numeratorBytes, denominatorBytes).ToArray();
+ return new[] { new TiffGenDataBlock(bytes) };
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs
index 84ea0f1acd..c442916402 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs
@@ -5,8 +5,6 @@
namespace ImageSharp.Tests
{
- using System;
- using System.IO;
using System.Linq;
///
@@ -17,7 +15,18 @@ namespace ImageSharp.Tests
public static TiffGenIfd WithoutEntry(this TiffGenIfd ifd, ushort tag)
{
TiffGenEntry entry = ifd.Entries.First(e => e.Tag == tag);
- ifd.Entries.Remove(entry);
+ if (entry != null)
+ {
+ ifd.Entries.Remove(entry);
+ }
+ return ifd;
+ }
+
+ public static TiffGenIfd WithEntry(this TiffGenIfd ifd, TiffGenEntry entry)
+ {
+ ifd.WithoutEntry(entry.Tag);
+ ifd.Entries.Add(entry);
+
return ifd;
}
}