Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
90 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
a61e83a
Merge pull request #119 from Andy-Wilkinson/tiff-codec
JimBobSquarePants Dec 10, 2018
904f5be
Merge branch 'master' into tiff-codec
JimBobSquarePants Jan 28, 2019
51720a8
Implement decoder tests for debugging tests and add deflate bug outpu…
IldarKhayrutdinov Aug 21, 2019
5e4c90f
Add TiffConfigurationModule to core Configuration. Implement simple u…
IldarKhayrutdinov Aug 21, 2019
104cac1
Add benchmarks for big and medium tiff files
IldarKhayrutdinov Aug 11, 2019
ab55bef
Move Tiff classes to Formats.Tiff namespace
IldarKhayrutdinov Aug 11, 2019
e101736
Mark Tiff format tests with "Tiff" category, cleanup test classes
IldarKhayrutdinov Aug 11, 2019
1b32629
Improve performance of Tiff decoders
IldarKhayrutdinov Aug 11, 2019
98e6d35
Merge branch 'master' of https://github.com/SixLabors/ImageSharp into…
IldarKhayrutdinov Aug 22, 2019
f75a17b
Fix of build error, cleanup.
IldarKhayrutdinov Aug 22, 2019
e8d45db
Implement temporary tiff native metadata structures
IldarKhayrutdinov Aug 22, 2019
ae820db
Mark missed tiff tests, exclude DecodeManual test
IldarKhayrutdinov Aug 22, 2019
5a601ef
Update test images submodule
IldarKhayrutdinov Aug 23, 2019
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
Add TIFF IFD encoding
  • Loading branch information
Andy-Wilkinson committed Jun 19, 2017
commit dfaa4cf835125bceedca34eecc30609bb9620088
5 changes: 0 additions & 5 deletions src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,5 @@ internal static class TiffConstants
/// Size (in bytes) of the Double data type
/// </summary>
public const int SizeOfDouble = 8;

/// <summary>
/// Size (in bytes) of the word boundary to allign data to when required
/// </summary>
public const int SizeOfWordBoundary = 4;
}
}
88 changes: 77 additions & 11 deletions src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -50,30 +51,95 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));

using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
using (TiffWriter writer = new TiffWriter(stream))
{
this.WriteHeader(writer, 0);
long firstIfdMarker = this.WriteHeader(writer);
long nextIfdMarker = this.WriteImage(writer, image, firstIfdMarker);
}
}

/// <summary>
/// Writes the TIFF file header.
/// </summary>
/// <param name="writer">The <see cref="BinaryWriter"/> to write data to.</param>
/// <param name="firstIfdOffset">The byte offset to the first IFD in the file.</param>
public void WriteHeader(BinaryWriter writer, uint firstIfdOffset)
/// <param name="writer">The <see cref="TiffWriter"/> to write data to.</param>
/// <returns>The marker to write the first IFD offset.</returns>
public long WriteHeader(TiffWriter writer)
{
if (firstIfdOffset == 0 || firstIfdOffset % TiffConstants.SizeOfWordBoundary != 0)
{
throw new ArgumentException("IFD offsets must be non-zero and on a word boundary.", nameof(firstIfdOffset));
}

ushort byteOrderMarker = BitConverter.IsLittleEndian ? TiffConstants.ByteOrderLittleEndianShort
: TiffConstants.ByteOrderBigEndianShort;

writer.Write(byteOrderMarker);
writer.Write((ushort)42);
writer.Write(firstIfdOffset);
long firstIfdMarker = writer.PlaceMarker();

return firstIfdMarker;
}

/// <summary>
/// Writes a TIFF IFD block.
/// </summary>
/// <param name="writer">The <see cref="BinaryWriter"/> to write data to.</param>
/// <param name="entries">The IFD entries to write to the file.</param>
/// <returns>The marker to write the next IFD offset (if present).</returns>
public long WriteIfd(TiffWriter writer, List<TiffIfdEntry> entries)
{
if (entries.Count == 0)
{
throw new ArgumentException("There must be at least one entry per IFD.", nameof(entries));
}

uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12));
List<byte[]> largeDataBlocks = new List<byte[]>();

entries.Sort((a, b) => a.Tag - b.Tag);

writer.Write((ushort)entries.Count);

foreach (TiffIfdEntry entry in entries)
{
writer.Write(entry.Tag);
writer.Write((ushort)entry.Type);
writer.Write(entry.Count);

if (entry.Value.Length <= 4)
{
writer.WritePadded(entry.Value);
}
else
{
largeDataBlocks.Add(entry.Value);
writer.Write(dataOffset);
dataOffset += (uint)(entry.Value.Length + (entry.Value.Length % 2));
}
}

long nextIfdMarker = writer.PlaceMarker();

foreach (byte[] dataBlock in largeDataBlocks)
{
writer.Write(dataBlock);

if (dataBlock.Length % 2 == 1)
{
writer.Write((byte)0);
}
}

return nextIfdMarker;
}

/// <summary>
/// Writes all data required to define an image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="BinaryWriter"/> to write data to.</param>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="ifdOffset">The marker to write this IFD offset.</param>
/// <returns>The marker to write the next IFD offset (if present).</returns>
public long WriteImage<TPixel>(TiffWriter writer, Image<TPixel> image, long ifdOffset)
where TPixel : struct, IPixel<TPixel>
{
throw new NotImplementedException();
}
}
}
109 changes: 109 additions & 0 deletions src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// <copyright file="TiffWriter.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Tiff
{
using System;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary>
internal class TiffWriter : IDisposable
{
private readonly Stream output;

private readonly byte[] paddingBytes = new byte[4];

private readonly List<long> references = new List<long>();

/// <summary>Initializes a new instance of the <see cref="TiffWriter"/> class.</summary>
/// <param name="output">The output stream.</param>
public TiffWriter(Stream output)
{
this.output = output;
}

/// <summary>
/// Gets a flag indicating whether the architecture is little-endian.
/// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian;

/// <summary>
/// Returns the current position within the stream.
/// </summary>
public long Position => this.output.Position;

/// <summary>Writes an empty four bytes to the stream, returning the offset to be written later.</summary>
/// <returns>The offset to be written later</returns>
public long PlaceMarker()
{
long offset = this.output.Position;
this.Write(0u);
return offset;
}

/// <summary>Writes an array of bytes to the current stream.</summary>
/// <param name="value">The bytes to write.</param>
public void Write(byte[] value)
{
this.output.Write(value, 0, value.Length);
}

/// <summary>Writes a byte to the current stream.</summary>
/// <param name="value">The byte to write.</param>
public void Write(byte value)
{
this.output.Write(new byte[] { value }, 0, 1);
}

/// <summary>Writes a two-byte unsigned integer to the current stream.</summary>
/// <param name="value">The two-byte unsigned integer to write.</param>
public void Write(ushort value)
{
byte[] bytes = BitConverter.GetBytes(value);
this.output.Write(bytes, 0, 2);
}

/// <summary>Writes a four-byte unsigned integer to the current stream.</summary>
/// <param name="value">The four-byte unsigned integer to write.</param>
public void Write(uint value)
{
byte[] bytes = BitConverter.GetBytes(value);
this.output.Write(bytes, 0, 4);
}

/// <summary>Writes an array of bytes to the current stream, padded to four-bytes.</summary>
/// <param name="value">The bytes to write.</param>
public void WritePadded(byte[] value)
{
this.output.Write(value, 0, value.Length);

if (value.Length < 4)
{
this.output.Write(this.paddingBytes, 0, 4 - value.Length);
}
}

/// <summary>Writes a four-byte unsigned integer to the specified marker in the stream.</summary>
/// <param name="offset">The offset returned when placing the marker</param>
/// <param name="value">The four-byte unsigned integer to write.</param>
public void WriteMarker(long offset, uint value)
{
long currentOffset = this.output.Position;
this.output.Seek(offset, SeekOrigin.Begin);
this.Write(value);
this.output.Seek(currentOffset, SeekOrigin.Begin);
}

/// <summary>
/// Disposes <see cref="TiffWriter"/> instance, ensuring any unwritten data is flushed.
/// </summary>
public void Dispose()
{
this.output.Flush();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ namespace ImageSharp.Tests
{
using System;
using System.IO;
using System.Linq;
using Xunit;

using ImageSharp.Formats;
using ImageSharp.Formats.Tiff;
using System.Text;

public class TiffEncoderHeaderTests
Expand All @@ -20,41 +22,24 @@ public void WriteHeader_WritesValidHeader()
MemoryStream stream = new MemoryStream();
TiffEncoderCore encoder = new TiffEncoderCore(null);

using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
using (TiffWriter writer = new TiffWriter(stream))
{
encoder.WriteHeader(writer, 1232);
long firstIfdMarker = encoder.WriteHeader(writer);
}

stream.Position = 0;
Assert.Equal(8, stream.Length);
Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0xD0, 0x04, 0x00, 0x00 }, stream.ToArray());
Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0x00, 0x00, 0x00, 0x00 }, stream.ToArray());
}

[Fact]
public void WriteHeader_ThrowsExceptionIfFirstIfdOffsetIsZero()
public void WriteHeader_ReturnsFirstIfdMarker()
{
MemoryStream stream = new MemoryStream();
TiffEncoderCore encoder = new TiffEncoderCore(null);

using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
using (TiffWriter writer = new TiffWriter(stream))
{
ArgumentException e = Assert.Throws<ArgumentException>(() => { encoder.WriteHeader(writer, 0); });
Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message);
Assert.Equal("firstIfdOffset", e.ParamName);
}
}

[Fact]
public void WriteHeader_ThrowsExceptionIfIfdOffsetIsNotOnAWordBoundary()
{
MemoryStream stream = new MemoryStream();
TiffEncoderCore encoder = new TiffEncoderCore(null);

using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
{
ArgumentException e = Assert.Throws<ArgumentException>(() => { encoder.WriteHeader(writer, 1234); });
Assert.Equal("IFD offsets must be non-zero and on a word boundary.\r\nParameter name: firstIfdOffset", e.Message);
Assert.Equal("firstIfdOffset", e.ParamName);
long firstIfdMarker = encoder.WriteHeader(writer);
Assert.Equal(4, firstIfdMarker);
}
}
}
Expand Down
Loading