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)
{
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;
}
}
}
}

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);
}
[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);
}
}

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

@ -11,6 +11,7 @@
<ItemGroup>
<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.runner.visualstudio" Version="2.2.0-beta5-build1225" />
</ItemGroup>

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

@ -16,12 +16,14 @@ namespace ImageSharp.Tests
/// </summary>
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<TiffGenDataBlock> 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<TiffGenDataBlock> 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;
}

13
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));
}

Loading…
Cancel
Save