Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
a74d143
Add initial Tiff codec projects, and TiffGen
Andy-Wilkinson Feb 26, 2017
8cbfc8e
Add Tiff implementation of IImageFormat
Andy-Wilkinson Feb 28, 2017
0196499
Reference ImageSharp (temporary code sharing)
Andy-Wilkinson Mar 2, 2017
4f6c75a
Add stub Tiff encoders/decoders
Andy-Wilkinson Mar 2, 2017
7e3d2c0
Read and validate the TIFF file header.
Andy-Wilkinson Mar 4, 2017
e976a2c
Make Tiff implementation details internal
Andy-Wilkinson Mar 4, 2017
be842dc
Make TiffDecoderCore more unit-testable
Andy-Wilkinson Mar 5, 2017
5e0fc3b
Read raw data from TIFF IFDs
Andy-Wilkinson Mar 5, 2017
e4e2b4c
More comprehensive testing of header checks
Andy-Wilkinson Mar 5, 2017
0534ce0
Read raw bytes for IFD entries
Andy-Wilkinson Mar 6, 2017
e3d7436
Read integer types from TIFF IFDs
Andy-Wilkinson Mar 12, 2017
a637ef4
Read strings from TIFF IFDs
Andy-Wilkinson Mar 12, 2017
fe6d5f6
Read rational numbers from TIFF IFDs
Andy-Wilkinson Mar 13, 2017
6792d88
Use constants for data type sizes
Andy-Wilkinson Mar 13, 2017
90a3dc9
Read floating-point numbers from TIFF IFDs
Andy-Wilkinson Mar 13, 2017
8cec9d6
Add a number of TIFF specific constants/enums
Andy-Wilkinson Mar 14, 2017
1cb02a4
Merge branch 'master' into tiff-codec
Andy-Wilkinson Mar 14, 2017
736019e
Incorporate Tiff codec into new project structure
Andy-Wilkinson Mar 14, 2017
94847e8
Remove unneeded using statements
Andy-Wilkinson Mar 14, 2017
1646c67
Fix many StyleCop warnings!
Andy-Wilkinson Mar 14, 2017
3b51c36
Add documentation to all elements.
Andy-Wilkinson Mar 18, 2017
aef9284
Run TIFF unit tests in CI
Andy-Wilkinson Mar 18, 2017
a874148
Fix project path for CI coverage script
Andy-Wilkinson Mar 18, 2017
c3a980c
Alternatively... Just merge in the TIFF tests
Andy-Wilkinson Mar 20, 2017
c582ecf
Decode TIFF image dimensions.
Andy-Wilkinson Mar 20, 2017
143a73e
Decode TIFF image resolution
Andy-Wilkinson Mar 29, 2017
959a560
Implement image decoding for the most basic TIFF image format.
Andy-Wilkinson Apr 1, 2017
b71a644
Add Image extensions to save TIFF format files
Andy-Wilkinson Apr 1, 2017
2d6a661
Move TIFF internals into own namespace
Andy-Wilkinson Apr 4, 2017
1322e07
Merge branch 'master' into tiff-codec
Andy-Wilkinson Apr 4, 2017
6979a72
Update TIFF codec to new IImageDecoder signature
Andy-Wilkinson Apr 4, 2017
e00ce80
Add support for WhiteIsZero bilevel & 4-bit images
Andy-Wilkinson Apr 14, 2017
2f1379a
Add support for PackBits compression
Andy-Wilkinson Apr 14, 2017
756b8ab
Add support for multi-strip TIFF files
Andy-Wilkinson Apr 14, 2017
2efc1f0
Merge branch 'master' into tiff-codec
Andy-Wilkinson May 15, 2017
b410526
Update namespaces and naming
Andy-Wilkinson May 15, 2017
da4b738
Add default WhiteIsZero implementation
Andy-Wilkinson May 15, 2017
a579df3
Add support for BlackIsZero
Andy-Wilkinson May 15, 2017
d241c19
Add support for pallete color images
Andy-Wilkinson May 16, 2017
64e0e55
Add support for RGB full color images
Andy-Wilkinson May 16, 2017
933f54b
Add optimised implementation for 8-bit RGB images
Andy-Wilkinson May 16, 2017
2147169
Add support for Deflate (and OldDeflate) compression
Andy-Wilkinson May 21, 2017
d1e0cc6
Add support for RGB (planar) pixel formats
Andy-Wilkinson May 25, 2017
40085b2
Add helper function when reading entries with defaults
Andy-Wilkinson May 25, 2017
ee295f2
Merge branch 'master' into tiff-codec
Andy-Wilkinson May 25, 2017
024d268
PixelAccessor needs to be internal in unit tests
Andy-Wilkinson May 25, 2017
96b3e06
Add support for LZW compression
Andy-Wilkinson May 30, 2017
0ae6220
Add TIFF README.md file
Andy-Wilkinson May 31, 2017
70b8207
Add more items to TIFF readme
Andy-Wilkinson May 31, 2017
743801f
Fill TIFF Readme tables with current status.
Andy-Wilkinson Jun 5, 2017
faee97c
Refactor TIFF metadata reading into separate method
Andy-Wilkinson Jun 5, 2017
f986ac7
Read baseline TIFF metadata
Andy-Wilkinson Jun 6, 2017
49867fb
Merge branch 'master' into tiff-codec
Andy-Wilkinson Jun 6, 2017
38a89f1
Use new pixel packing methods
Andy-Wilkinson Jun 6, 2017
0971b9d
Add some TIFF sample images (autogenerated)
Andy-Wilkinson Jun 6, 2017
16319d0
Add TIFF as a default image format
Andy-Wilkinson Jun 6, 2017
cba51ed
Add 'DecodeTiff' benchmark
Andy-Wilkinson Jun 7, 2017
719e6af
Add TiffEncoder and write TIFF header
Andy-Wilkinson Jun 7, 2017
dfaa4cf
Add TIFF IFD encoding
Andy-Wilkinson Jun 19, 2017
afbe283
Write metadata to a TIFF file
Andy-Wilkinson Jun 22, 2017
8be5d3d
Update xUnit version (and fix test warnings)
Andy-Wilkinson Jul 4, 2017
32ab03b
Merge from latest master
Andy-Wilkinson Jul 5, 2017
a3189de
Use new configuration API for TIFF codec
Andy-Wilkinson Jul 5, 2017
5ca5f77
Merge branch 'master' into tiff-codec
JimBobSquarePants Feb 8, 2018
e4bb5e4
Merge branch 'master' into tiff-codec
JimBobSquarePants Feb 9, 2018
affd736
Merge branch 'master' of https://github.com/SixLabors/ImageSharp into…
JimBobSquarePants Mar 4, 2018
64094c5
Update codebase to catch up with changes to main repo.
JimBobSquarePants Mar 4, 2018
5fa27fb
Use environment specific newline in test
JimBobSquarePants Mar 4, 2018
3bd7f72
Merge branch 'master' into tiff-codec
JimBobSquarePants Mar 4, 2018
6b212c6
Merge branch 'master' of https://github.com/SixLabors/ImageSharp into…
JimBobSquarePants Mar 14, 2018
554318b
Fix namespace reference in tests
JimBobSquarePants Mar 14, 2018
8a513a5
Merge branch 'master' into tiff-codec
JimBobSquarePants Aug 31, 2018
bc7bafe
Merge remote-tracking branch 'upstream/master' into tiff-codec
JimBobSquarePants Oct 17, 2018
ca00f9b
Update tests/Images/External
JimBobSquarePants Oct 18, 2018
f0ead2d
Merge remote-tracking branch 'upstream/master' into tiff-codec
JimBobSquarePants Dec 6, 2018
baf6239
Update IPixel method calls to match new signatures
JimBobSquarePants Dec 6, 2018
a8d25ef
Update external references
JimBobSquarePants Dec 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Read raw data from TIFF IFDs
  • Loading branch information
Andy-Wilkinson committed Mar 5, 2017
commit 5e0fc3b9fa3a64b2488c03d7722a27a8c40ec6ed
10 changes: 10 additions & 0 deletions src/ImageSharp.Formats.Tiff/TiffConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,15 @@ internal static class TiffConstants
/// 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 changes: 32 additions & 5 deletions src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public void Decode<TColor>(Image<TColor> image, Stream stream, bool metadataOnly
this.InputStream = stream;

uint firstIfdOffset = ReadHeader();
TiffIfd firstIfd = ReadIfd(firstIfdOffset);
}

/// <summary>
Expand All @@ -70,8 +71,8 @@ public void Dispose()

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;
Expand All @@ -88,7 +89,35 @@ public uint ReadHeader()
return firstIfdOffset;
}

private byte[] ReadBytes(byte[] buffer, int count)
public TiffIfd ReadIfd(uint offset)
{
InputStream.Seek(offset, SeekOrigin.Begin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can't assume that the TIFF image starts at the beginning of the stream. (Or at least we don't do this in other codecs.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we can't assume that. Someone could have multiple images in a stream or other information. Best we can do is look for the identifier at the position we are currently at.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay... The easy fix is to store the offset that we start at, and do all Seeks relative to this, i.e. InputStream.Seek(StartOffset + offset, SeekOrigin.Begin).

I'd really love to get rid of the Seeks entirely, but unfortunately the TIFF file-format relies heavily on referencing offsets within the file. There is no way you can tell what the next bit of data is otherwise. As an example a file layout could be,

0-8 -> TIFF file header (8 bytes) - references first IFD at offset 30000
8-10000 -> Raw image data here
30000-... -> IFD (basically an image header) - references image data at offset 8

Until you read the IFD at offset 30000, you have no way of telling what data is at offset 8 - it could be image data in an unknown compression/format, another IFD, strings from the metadata. The format doesn't have any identifiers to let you know until you have read the IFD itself.

Any clever ideas to avoid Seeks would be great though! 😄


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;

Expand All @@ -102,8 +131,6 @@ private byte[] ReadBytes(byte[] buffer, int count)
offset += bytesRead;
count -= bytesRead;
}

return buffer;
}

private Int16 ToInt16(byte[] bytes, int offset)
Expand Down
22 changes: 22 additions & 0 deletions src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfd.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions src/ImageSharp.Formats.Tiff/TiffIfd/TiffIfdEntry.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}