diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
index 0f9145208..73508b34a 100644
--- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
@@ -26,5 +26,15 @@ namespace ImageSharp.Formats
/// Magic number used within the image file header to identify a TIFF format file.
///
public const ushort HeaderMagicNumber = 42;
+
+ ///
+ /// Size (in bytes) of the TIFF file header.
+ ///
+ public const int SizeOfTiffHeader = 8;
+
+ ///
+ /// Size (in bytes) of each individual TIFF IFD entry
+ ///
+ public const int SizeOfIfdEntry = 12;
}
}
diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
index aee57b1ea..e8c0db788 100644
--- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
@@ -59,6 +59,7 @@ namespace ImageSharp.Formats
this.InputStream = stream;
uint firstIfdOffset = ReadHeader();
+ TiffIfd firstIfd = ReadIfd(firstIfdOffset);
}
///
@@ -70,8 +71,8 @@ namespace ImageSharp.Formats
public uint ReadHeader()
{
- byte[] headerBytes = new byte[8];
- ReadBytes(headerBytes, 8);
+ byte[] headerBytes = new byte[TiffConstants.SizeOfTiffHeader];
+ ReadBytes(headerBytes, TiffConstants.SizeOfTiffHeader);
if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian)
IsLittleEndian = true;
@@ -88,7 +89,35 @@ namespace ImageSharp.Formats
return firstIfdOffset;
}
- private byte[] ReadBytes(byte[] buffer, int count)
+ public TiffIfd ReadIfd(uint offset)
+ {
+ InputStream.Seek(offset, SeekOrigin.Begin);
+
+ byte[] buffer = new byte[TiffConstants.SizeOfIfdEntry];
+
+ ReadBytes(buffer, 2);
+ ushort entryCount = ToUInt16(buffer, 0);
+
+ TiffIfdEntry[] entries = new TiffIfdEntry[entryCount];
+ for (int i = 0 ; i
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Data structure for holding details of each TIFF IFD
+ ///
+ internal struct TiffIfd
+ {
+ public TiffIfdEntry[] Entries;
+ public uint NextIfdOffset;
+
+ public TiffIfd(TiffIfdEntry[] entries, uint nextIfdOffset)
+ {
+ this.Entries = entries;
+ this.NextIfdOffset = nextIfdOffset;
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs b/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs
new file mode 100644
index 000000000..04686a4da
--- /dev/null
+++ b/src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs
@@ -0,0 +1,26 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Data structure for holding details of each TIFF IFD entry
+ ///
+ internal struct TiffIfdEntry
+ {
+ public ushort Tag;
+ public TiffType Type;
+ public uint Count;
+ public byte[] Value;
+
+ public TiffIfdEntry(ushort tag, TiffType type, uint count, byte[] value)
+ {
+ this.Tag = tag;
+ this.Type = type;
+ this.Count = count;
+ this.Value = value;
+ }
+ }
+}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
new file mode 100644
index 000000000..af0e0b93f
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
@@ -0,0 +1,111 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System.IO;
+ using System.Linq;
+ using Xunit;
+
+ using ImageSharp.Formats;
+
+ public class TiffDecoderIfdTests
+ {
+ public static object[][] IsLittleEndianValues = new[] { new object[] { false },
+ new object[] { true } };
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void ReadIfd_ReadsNextIfdOffset_IfPresent(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenIfd()
+ {
+ Entries =
+ {
+ TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150)
+ },
+ NextIfd = new TiffGenIfd()
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfd ifd = decoder.ReadIfd(0);
+
+ Assert.Equal(18u, ifd.NextIfdOffset);
+ }
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void ReadIfd_ReadsNextIfdOffset_ZeroIfLastIfd(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenIfd()
+ {
+ Entries =
+ {
+ TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150)
+ }
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfd ifd = decoder.ReadIfd(0);
+
+ Assert.Equal(0u, ifd.NextIfdOffset);
+ }
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void ReadIfd_ReturnsCorrectNumberOfEntries(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenIfd()
+ {
+ Entries =
+ {
+ TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150),
+ TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210),
+ TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1),
+ TiffGenEntry.Ascii(TiffTags.Artist, "Image Artist Name"),
+ TiffGenEntry.Ascii(TiffTags.HostComputer, "Host Computer Name")
+ },
+ NextIfd = new TiffGenIfd()
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfd ifd = decoder.ReadIfd(0);
+
+ Assert.NotNull(ifd.Entries);
+ Assert.Equal(5, ifd.Entries.Length);
+ }
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenIfd()
+ {
+ Entries =
+ {
+ TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150),
+ TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210),
+ TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1)
+ },
+ NextIfd = new TiffGenIfd()
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfd ifd = decoder.ReadIfd(0);
+ TiffIfdEntry entry = ifd.Entries[1];
+
+ byte[] expectedData = isLittleEndian ? new byte[] {210,0,0,0} : new byte[] {0,0,0,210};
+ Assert.NotNull(entry);
+ Assert.Equal(TiffTags.ImageLength, entry.Tag);
+ Assert.Equal(TiffType.Long, entry.Type);
+ Assert.Equal(4u, entry.Count);
+ Assert.Equal(expectedData, entry.Value);
+ }
+ }
+}
\ No newline at end of file