diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
index e8c0db788e..37aad73200 100644
--- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
@@ -133,6 +133,23 @@ namespace ImageSharp.Formats
}
}
+ public byte[] ReadBytes(ref TiffIfdEntry entry)
+ {
+ uint byteLength = GetSizeOfData(entry);
+
+ if (entry.Value.Length < byteLength)
+ {
+ uint offset = ToUInt32(entry.Value, 0);
+ InputStream.Seek(offset, SeekOrigin.Begin);
+
+ byte[] data = new byte[byteLength];
+ ReadBytes(data, (int)byteLength);
+ entry.Value = data;
+ }
+
+ return entry.Value;
+ }
+
private Int16 ToInt16(byte[] bytes, int offset)
{
if (IsLittleEndian)
@@ -158,5 +175,33 @@ namespace ImageSharp.Formats
{
return (ushort)ToInt16(bytes, offset);
}
+
+ public static uint GetSizeOfData(TiffIfdEntry entry) => SizeOfDataType(entry.Type) * entry.Count;
+
+ private static uint SizeOfDataType(TiffType type)
+ {
+ switch (type)
+ {
+ case TiffType.Byte:
+ case TiffType.Ascii:
+ case TiffType.SByte:
+ case TiffType.Undefined:
+ return 1u;
+ case TiffType.Short:
+ case TiffType.SShort:
+ return 2u;
+ case TiffType.Long:
+ case TiffType.SLong:
+ case TiffType.Float:
+ case TiffType.Ifd:
+ return 4u;
+ case TiffType.Rational:
+ case TiffType.SRational:
+ case TiffType.Double:
+ return 8u;
+ default:
+ return 0u;
+ }
+ }
}
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
new file mode 100644
index 0000000000..1d987de9af
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
@@ -0,0 +1,135 @@
+//
+// 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 TiffDecoderIfdEntryTests
+ {
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte, 1u, 1u)]
+ [InlineDataAttribute(TiffType.Ascii, 1u, 1u)]
+ [InlineDataAttribute(TiffType.Short, 1u, 2u)]
+ [InlineDataAttribute(TiffType.Long, 1u, 4u)]
+ [InlineDataAttribute(TiffType.Rational, 1u, 8u)]
+ [InlineDataAttribute(TiffType.SByte, 1u, 1u)]
+ [InlineDataAttribute(TiffType.Undefined, 1u, 1u)]
+ [InlineDataAttribute(TiffType.SShort, 1u, 2u)]
+ [InlineDataAttribute(TiffType.SLong, 1u, 4u)]
+ [InlineDataAttribute(TiffType.SRational, 1u, 8u)]
+ [InlineDataAttribute(TiffType.Float, 1u, 4u)]
+ [InlineDataAttribute(TiffType.Double, 1u, 8u)]
+ [InlineDataAttribute(TiffType.Ifd, 1u, 4u)]
+ [InlineDataAttribute((TiffType)999, 1u, 0u)]
+ public void GetSizeOfData_SingleItem_ReturnsCorrectSize(ushort type, uint count, uint expectedSize)
+ {
+ TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]);
+ uint size = TiffDecoderCore.GetSizeOfData(entry);
+ Assert.Equal(expectedSize, size);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte, 15u, 15u)]
+ [InlineDataAttribute(TiffType.Ascii, 20u, 20u)]
+ [InlineDataAttribute(TiffType.Short, 18u, 36u)]
+ [InlineDataAttribute(TiffType.Long, 4u, 16u)]
+ [InlineDataAttribute(TiffType.Rational, 9u, 72u)]
+ [InlineDataAttribute(TiffType.SByte, 5u, 5u)]
+ [InlineDataAttribute(TiffType.Undefined, 136u, 136u)]
+ [InlineDataAttribute(TiffType.SShort, 12u, 24u)]
+ [InlineDataAttribute(TiffType.SLong, 15u, 60u)]
+ [InlineDataAttribute(TiffType.SRational, 10u, 80u)]
+ [InlineDataAttribute(TiffType.Float, 2u, 8u)]
+ [InlineDataAttribute(TiffType.Double, 2u, 16u)]
+ [InlineDataAttribute(TiffType.Ifd, 10u, 40u)]
+ [InlineDataAttribute((TiffType)999, 1050u, 0u)]
+ public void GetSizeOfData_Array_ReturnsCorrectSize(ushort type, uint count, uint expectedSize)
+ {
+ TiffIfdEntry entry = new TiffIfdEntry(TiffTags.ImageWidth, (TiffType)type, count, new byte[4]);
+ uint size = TiffDecoderCore.GetSizeOfData(entry);
+ Assert.Equal(expectedSize, size);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 1u, new byte[] { 17 }, true)]
+ [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 2u, new byte[] { 17, 28 }, true)]
+ [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 4u, new byte[] { 17, 28, 2, 9 }, true)]
+ [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)]
+ [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)]
+ [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, false)]
+ [InlineDataAttribute(TiffType.Short, 1u, new byte[] { 17, 28 }, true)]
+ [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, false)]
+ [InlineDataAttribute(TiffType.Short, 2u, new byte[] { 17, 28, 2, 9 }, true)]
+ [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)]
+ [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)]
+ [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, false)]
+ [InlineDataAttribute(TiffType.Long, 1u, new byte[] { 17, 28, 2, 9 }, true)]
+ [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)]
+ [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)]
+ [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)]
+ [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)]
+ public void ReadBytes_ReturnsExpectedData(ushort type, uint count, byte[] bytes, bool isLittleEndian)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian);
+
+ byte[] result = decoder.ReadBytes(ref entry);
+
+ if (bytes.Length < 4)
+ result = result.Take(bytes.Length).ToArray();
+
+ Assert.Equal(bytes, result);
+ }
+
+ [Theory]
+ [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 5u, new byte[] { 17, 28, 2, 9, 13 }, true)]
+ [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, false)]
+ [InlineDataAttribute(TiffType.Byte, 10u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2, 127, 86 }, true)]
+ [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, false)]
+ [InlineDataAttribute(TiffType.Short, 3u, new byte[] { 17, 28, 2, 9, 13, 37 }, true)]
+ [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)]
+ [InlineDataAttribute(TiffType.Long, 2u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)]
+ [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, false)]
+ [InlineDataAttribute(TiffType.Rational, 1u, new byte[] { 17, 28, 2, 9, 13, 37, 18, 2 }, true)]
+ public void ReadBytes_CachesDataLongerThanFourBytes(ushort type, uint count, byte[] bytes, bool isLittleEndian)
+ {
+ (TiffDecoderCore decoder, TiffIfdEntry entry) = GenerateTestIfdEntry(TiffGenEntry.Bytes(TiffTags.ImageWidth, (TiffType)type, count, bytes), isLittleEndian);
+
+ Assert.Equal(4, entry.Value.Length);
+
+ byte[] result = decoder.ReadBytes(ref entry);
+
+ Assert.Equal(bytes.Length, entry.Value.Length);
+ Assert.Equal(bytes, entry.Value);
+ }
+
+ private (TiffDecoderCore, TiffIfdEntry) GenerateTestIfdEntry(TiffGenEntry entry, bool isLittleEndian)
+ {
+ Stream stream = new TiffGenIfd()
+ {
+ Entries =
+ {
+ entry
+ }
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
+ TiffIfdEntry ifdEntry = decoder.ReadIfd(0).Entries[0];
+
+ return (decoder, ifdEntry);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
index af0e0b93f0..606e024a14 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
@@ -80,7 +80,7 @@ namespace ImageSharp.Tests
Assert.Equal(5, ifd.Entries.Length);
}
- [Theory]
+ [Theory]
[MemberData(nameof(IsLittleEndianValues))]
public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian)
{
@@ -104,7 +104,7 @@ namespace ImageSharp.Tests
Assert.NotNull(entry);
Assert.Equal(TiffTags.ImageLength, entry.Tag);
Assert.Equal(TiffType.Long, entry.Type);
- Assert.Equal(4u, entry.Count);
+ Assert.Equal(1u, entry.Count);
Assert.Equal(expectedData, entry.Value);
}
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj
index a20f1fd99f..30ec6b75f5 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj
+++ b/tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj
@@ -11,6 +11,7 @@
+
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
index df61b4d8a6..757da63b4d 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
@@ -16,12 +16,14 @@ namespace ImageSharp.Tests
///
internal abstract class TiffGenEntry : ITiffGenDataSource
{
- private TiffGenEntry(ushort tag, TiffType type)
+ private TiffGenEntry(ushort tag, TiffType type, uint count)
{
this.Tag = tag;
this.Type = type;
+ this.Count = count;
}
+ public uint Count { get; }
public ushort Tag { get; }
public TiffType Type { get; }
@@ -32,6 +34,11 @@ namespace ImageSharp.Tests
return new TiffGenEntryAscii(tag, value);
}
+ public static TiffGenEntry Bytes(ushort tag, TiffType type, uint count, byte[] value)
+ {
+ return new TiffGenEntryBytes(tag, type, count, value);
+ }
+
public static TiffGenEntry Integer(ushort tag, TiffType type, int value)
{
return TiffGenEntry.Integer(tag, type, new int[] {value});
@@ -48,7 +55,7 @@ namespace ImageSharp.Tests
private class TiffGenEntryAscii : TiffGenEntry
{
- public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii)
+ public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii, (uint)GetBytes(value).Length)
{
this.Value = value;
}
@@ -57,14 +64,34 @@ namespace ImageSharp.Tests
public override IEnumerable GetData(bool isLittleEndian)
{
- byte[] bytes = Encoding.ASCII.GetBytes($"{Value}\0");
+ byte[] bytes = GetBytes(Value);
return new[] { new TiffGenDataBlock(bytes) };
}
+
+ private static byte[] GetBytes(string value)
+ {
+ return Encoding.ASCII.GetBytes($"{value}\0");
+ }
+ }
+
+ private class TiffGenEntryBytes : TiffGenEntry
+ {
+ public TiffGenEntryBytes(ushort tag, TiffType type, uint count, byte[] value) : base(tag, type, count)
+ {
+ this.Value = value;
+ }
+
+ public byte[] Value { get; }
+
+ public override IEnumerable GetData(bool isLittleEndian)
+ {
+ return new[] { new TiffGenDataBlock(Value) };
+ }
}
private class TiffGenEntryInteger : TiffGenEntry
{
- public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type)
+ public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type, (uint)value.Length)
{
this.Value = value;
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs
index c26a0f1997..ee560b18fe 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs
@@ -40,18 +40,17 @@ namespace ImageSharp.Tests
{
var entryData = entry.GetData(isLittleEndian);
var entryBytes = entryData.First().Bytes;
- var entryCount = entryBytes.Length;
bytes.AddUInt16(entry.Tag);
bytes.AddUInt16((ushort)entry.Type);
- bytes.AddUInt32((uint)entryCount);
+ bytes.AddUInt32(entry.Count);
- if (entryCount <=4)
+ if (entryBytes.Length <=4)
{
- bytes.AddByte(entryCount > 0 ? entryBytes[0] : (byte)0);
- bytes.AddByte(entryCount > 1 ? entryBytes[1] : (byte)0);
- bytes.AddByte(entryCount > 2 ? entryBytes[2] : (byte)0);
- bytes.AddByte(entryCount > 3 ? entryBytes[3] : (byte)0);
+ bytes.AddByte(entryBytes.Length > 0 ? entryBytes[0] : (byte)0);
+ bytes.AddByte(entryBytes.Length > 1 ? entryBytes[1] : (byte)0);
+ bytes.AddByte(entryBytes.Length > 2 ? entryBytes[2] : (byte)0);
+ bytes.AddByte(entryBytes.Length > 3 ? entryBytes[3] : (byte)0);
dataBlocks.AddRange(entryData.Skip(1));
}