Browse Source

Read raw bytes for IFD entries

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
2a89db1a06
  1. 45
      src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
  2. 135
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs
  3. 4
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs
  4. 1
      tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj
  5. 35
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
  6. 13
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs

45
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) private Int16 ToInt16(byte[] bytes, int offset)
{ {
if (IsLittleEndian) if (IsLittleEndian)
@ -158,5 +175,33 @@ namespace ImageSharp.Formats
{ {
return (ushort)ToInt16(bytes, offset); 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;
}
}
} }
} }

135
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdEntryTests.cs

@ -0,0 +1,135 @@
// <copyright file="TiffDecoderIfdEntryTests.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.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);
}
}
}

4
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs

@ -80,7 +80,7 @@ namespace ImageSharp.Tests
Assert.Equal(5, ifd.Entries.Length); Assert.Equal(5, ifd.Entries.Length);
} }
[Theory] [Theory]
[MemberData(nameof(IsLittleEndianValues))] [MemberData(nameof(IsLittleEndianValues))]
public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian) public void ReadIfd_ReadsRawTiffEntryData(bool isLittleEndian)
{ {
@ -104,7 +104,7 @@ namespace ImageSharp.Tests
Assert.NotNull(entry); Assert.NotNull(entry);
Assert.Equal(TiffTags.ImageLength, entry.Tag); Assert.Equal(TiffTags.ImageLength, entry.Tag);
Assert.Equal(TiffType.Long, entry.Type); Assert.Equal(TiffType.Long, entry.Type);
Assert.Equal(4u, entry.Count); Assert.Equal(1u, entry.Count);
Assert.Equal(expectedData, entry.Value); Assert.Equal(expectedData, entry.Value);
} }
} }

1
tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170123-02" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170123-02" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.2.0-beta5-build3474" /> <PackageReference Include="xunit" Version="2.2.0-beta5-build3474" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-beta5-build1225" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-beta5-build1225" />
</ItemGroup> </ItemGroup>

35
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs

@ -16,12 +16,14 @@ namespace ImageSharp.Tests
/// </summary> /// </summary>
internal abstract class TiffGenEntry : ITiffGenDataSource internal abstract class TiffGenEntry : ITiffGenDataSource
{ {
private TiffGenEntry(ushort tag, TiffType type) private TiffGenEntry(ushort tag, TiffType type, uint count)
{ {
this.Tag = tag; this.Tag = tag;
this.Type = type; this.Type = type;
this.Count = count;
} }
public uint Count { get; }
public ushort Tag { get; } public ushort Tag { get; }
public TiffType Type { get; } public TiffType Type { get; }
@ -32,6 +34,11 @@ namespace ImageSharp.Tests
return new TiffGenEntryAscii(tag, value); 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) 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});
@ -48,7 +55,7 @@ namespace ImageSharp.Tests
private class TiffGenEntryAscii : TiffGenEntry 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; this.Value = value;
} }
@ -57,14 +64,34 @@ namespace ImageSharp.Tests
public override IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian) public override IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian)
{ {
byte[] bytes = Encoding.ASCII.GetBytes($"{Value}\0"); byte[] bytes = GetBytes(Value);
return new[] { new TiffGenDataBlock(bytes) }; 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<TiffGenDataBlock> GetData(bool isLittleEndian)
{
return new[] { new TiffGenDataBlock(Value) };
}
} }
private class TiffGenEntryInteger : TiffGenEntry 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; this.Value = value;
} }

13
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs

@ -40,18 +40,17 @@ namespace ImageSharp.Tests
{ {
var entryData = entry.GetData(isLittleEndian); var entryData = entry.GetData(isLittleEndian);
var entryBytes = entryData.First().Bytes; var entryBytes = entryData.First().Bytes;
var entryCount = entryBytes.Length;
bytes.AddUInt16(entry.Tag); bytes.AddUInt16(entry.Tag);
bytes.AddUInt16((ushort)entry.Type); 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(entryBytes.Length > 0 ? entryBytes[0] : (byte)0);
bytes.AddByte(entryCount > 1 ? entryBytes[1] : (byte)0); bytes.AddByte(entryBytes.Length > 1 ? entryBytes[1] : (byte)0);
bytes.AddByte(entryCount > 2 ? entryBytes[2] : (byte)0); bytes.AddByte(entryBytes.Length > 2 ? entryBytes[2] : (byte)0);
bytes.AddByte(entryCount > 3 ? entryBytes[3] : (byte)0); bytes.AddByte(entryBytes.Length > 3 ? entryBytes[3] : (byte)0);
dataBlocks.AddRange(entryData.Skip(1)); dataBlocks.AddRange(entryData.Skip(1));
} }

Loading…
Cancel
Save