Browse Source

Decode TIFF image dimensions.

pull/119/head
Andrew Wilkinson 9 years ago
parent
commit
c582ecf1f6
  1. 22
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  2. 21
      src/ImageSharp/Formats/Tiff/TiffIfd/TiffIfd.cs
  3. 84
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs
  4. 26
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs
  5. 61
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs
  6. 24
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs

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

@ -73,6 +73,7 @@ namespace ImageSharp.Formats
uint firstIfdOffset = this.ReadHeader();
TiffIfd firstIfd = this.ReadIfd(firstIfdOffset);
this.DecodeImage(firstIfd, image);
}
/// <summary>
@ -150,6 +151,27 @@ namespace ImageSharp.Formats
return new TiffIfd(entries, nextIfdOffset);
}
/// <summary>
/// Decodes the image data from a specified IFD.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="ifd">The IFD to read the image from.</param>
/// <param name="image">The image, where the data should be set to.</param>
public void DecodeImage<TColor>(TiffIfd ifd, Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
if (!ifd.TryGetIfdEntry(TiffTags.ImageLength, out TiffIfdEntry imageLengthEntry)
|| !ifd.TryGetIfdEntry(TiffTags.ImageWidth, out TiffIfdEntry imageWidthEntry))
{
throw new ImageFormatException("The TIFF IFD does not specify the image dimensions.");
}
int width = (int)this.ReadUnsignedInteger(ref imageWidthEntry);
int height = (int)this.ReadUnsignedInteger(ref imageLengthEntry);
image.InitPixels(width, height);
}
/// <summary>
/// Reads the data from a <see cref="TiffIfdEntry"/> as an array of bytes.
/// </summary>

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

@ -30,5 +30,26 @@ namespace ImageSharp.Formats
this.Entries = entries;
this.NextIfdOffset = nextIfdOffset;
}
/// <summary>
/// Gets the child <see cref="TiffIfdEntry"/> with the specified tag ID.
/// </summary>
/// <param name="tag">The tag ID to search for.</param>
/// <param name="entry">The resulting <see cref="TiffIfdEntry"/>, if it exists.</param>
/// <returns>A flag indicating whether the requested entry exists</returns>
public bool TryGetIfdEntry(ushort tag, out TiffIfdEntry entry)
{
for (int i = 0; i < this.Entries.Length; i++)
{
if (this.Entries[i].Tag == tag)
{
entry = this.Entries[i];
return true;
}
}
entry = default(TiffIfdEntry);
return false;
}
}
}

84
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs

@ -0,0 +1,84 @@
// <copyright file="TiffDecoderImageTests.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 Xunit;
using ImageSharp.Formats;
public class TiffDecoderImageTests
{
public const int ImageWidth = 200;
public const int ImageHeight = 150;
public static object[][] IsLittleEndianValues = new[] { new object[] { false },
new object[] { true } };
[Theory]
[MemberData(nameof(IsLittleEndianValues))]
public void DecodeImage_SetsImageDimensions(bool isLittleEndian)
{
Stream stream = CreateTiffGenIfd()
.ToStream(isLittleEndian);
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
Image<Color> image = new Image<Color>(1,1);
decoder.DecodeImage(ifd, image);
Assert.Equal(ImageWidth, image.Width);
Assert.Equal(ImageHeight, image.Height);
}
[Theory]
[MemberData(nameof(IsLittleEndianValues))]
public void DecodeImage_ThrowsException_WithMissingImageWidth(bool isLittleEndian)
{
Stream stream = CreateTiffGenIfd()
.WithoutEntry(TiffTags.ImageWidth)
.ToStream(isLittleEndian);
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
Image<Color> image = new Image<Color>(1,1);
var e = Assert.Throws<ImageFormatException>(() => decoder.DecodeImage(ifd, image));
Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message);
}
[Theory]
[MemberData(nameof(IsLittleEndianValues))]
public void DecodeImage_ThrowsException_WithMissingImageLength(bool isLittleEndian)
{
Stream stream = CreateTiffGenIfd()
.WithoutEntry(TiffTags.ImageLength)
.ToStream(isLittleEndian);
TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null);
TiffIfd ifd = decoder.ReadIfd(0);
Image<Color> image = new Image<Color>(1,1);
var e = Assert.Throws<ImageFormatException>(() => decoder.DecodeImage(ifd, image));
Assert.Equal("The TIFF IFD does not specify the image dimensions.", e.Message);
}
private TiffGenIfd CreateTiffGenIfd()
{
return new TiffGenIfd()
{
Entries =
{
TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, ImageWidth),
TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, ImageHeight),
}
};
}
}
}

26
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdEntryTests.cs

@ -0,0 +1,26 @@
// <copyright file="TiffIfdEntryTests.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 Xunit;
using ImageSharp.Formats;
public class TiffIfdEntryTests
{
[Fact]
public void Constructor_SetsProperties()
{
var entry = new TiffIfdEntry((ushort)10u, TiffType.Short, 20u, new byte[] { 2, 4, 6, 8 });
Assert.Equal(10u, entry.Tag);
Assert.Equal(TiffType.Short, entry.Type);
Assert.Equal(20u, entry.Count);
Assert.Equal(new byte[] { 2, 4, 6, 8 }, entry.Value);
}
}
}

61
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffIfd/TiffIfdTests.cs

@ -0,0 +1,61 @@
// <copyright file="TiffIfdTests.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 Xunit;
using ImageSharp.Formats;
public class TiffIfdTests
{
[Fact]
public void Constructor_SetsProperties()
{
var entries = new TiffIfdEntry[10];
var ifd = new TiffIfd(entries, 1234u);
Assert.Equal(entries, ifd.Entries);
Assert.Equal(1234u, ifd.NextIfdOffset);
}
[Fact]
public void TryGetIfdEntry_ReturnsIfdIfExists()
{
var entries = new[]
{
new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(40, TiffType.Short, 20, new byte[4])
};
var ifd = new TiffIfd(entries, 1234u);
bool success = ifd.TryGetIfdEntry(30, out var entry);
Assert.Equal(true, success);
Assert.Equal(30, entry.Tag);
}
[Fact]
public void TryGetIfdEntry_ReturnsFalseOtherwise()
{
var entries = new[]
{
new TiffIfdEntry(10, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(20, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(30, TiffType.Short, 20, new byte[4]),
new TiffIfdEntry(40, TiffType.Short, 20, new byte[4])
};
var ifd = new TiffIfd(entries, 1234u);
bool success = ifd.TryGetIfdEntry(25, out var entry);
Assert.Equal(false, success);
Assert.Equal(0, entry.Tag);
}
}
}

24
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfdExtensions.cs

@ -0,0 +1,24 @@
// <copyright file="TiffGenIfdExtensions.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;
using System.IO;
using System.Linq;
/// <summary>
/// A utility class for manipulating in-memory Tiff files for use in unit tests.
/// </summary>
internal static class TiffGenIfdExtensions
{
public static TiffGenIfd WithoutEntry(this TiffGenIfd ifd, ushort tag)
{
TiffGenEntry entry = ifd.Entries.First(e => e.Tag == tag);
ifd.Entries.Remove(entry);
return ifd;
}
}
}
Loading…
Cancel
Save