Browse Source

Read raw data from TIFF IFDs

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
dff3ca43cf
  1. 10
      src/ImageSharp.Formats.Tiff/TiffConstants.cs
  2. 37
      src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
  3. 22
      src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs
  4. 26
      src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs
  5. 111
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderIfdTests.cs

10
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.
/// </summary>
public const ushort HeaderMagicNumber = 42;
/// <summary>
/// Size (in bytes) of the TIFF file header.
/// </summary>
public const int SizeOfTiffHeader = 8;
/// <summary>
/// Size (in bytes) of each individual TIFF IFD entry
/// </summary>
public const int SizeOfIfdEntry = 12;
}
}

37
src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs

@ -59,6 +59,7 @@ namespace ImageSharp.Formats
this.InputStream = stream;
uint firstIfdOffset = ReadHeader();
TiffIfd firstIfd = ReadIfd(firstIfdOffset);
}
/// <summary>
@ -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<entryCount; i++)
{
ReadBytes(buffer, TiffConstants.SizeOfIfdEntry);
ushort tag = ToUInt16(buffer, 0);
TiffType type = (TiffType)ToUInt16(buffer, 2);
uint count = ToUInt32(buffer, 4);
byte[] value = new byte[] { buffer[8], buffer[9], buffer[10], buffer[11] };
entries[i] = new TiffIfdEntry(tag, type, count, value);
}
ReadBytes(buffer, 4);
uint nextIfdOffset = ToUInt32(buffer, 0);
return new TiffIfd(entries, nextIfdOffset);
}
private void ReadBytes(byte[] buffer, int count)
{
int offset = 0;
@ -102,8 +131,6 @@ namespace ImageSharp.Formats
offset += bytesRead;
count -= bytesRead;
}
return buffer;
}
private Int16 ToInt16(byte[] bytes, int offset)

22
src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs

@ -0,0 +1,22 @@
// <copyright file="TiffIfd.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Data structure for holding details of each TIFF IFD
/// </summary>
internal struct TiffIfd
{
public TiffIfdEntry[] Entries;
public uint NextIfdOffset;
public TiffIfd(TiffIfdEntry[] entries, uint nextIfdOffset)
{
this.Entries = entries;
this.NextIfdOffset = nextIfdOffset;
}
}
}

26
src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs

@ -0,0 +1,26 @@
// <copyright file="TiffIfdEntry.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Data structure for holding details of each TIFF IFD entry
/// </summary>
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;
}
}
}

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

@ -0,0 +1,111 @@
// <copyright file="TiffDecoderIfdTests.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 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);
}
}
}
Loading…
Cancel
Save