diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
index 10a3478c0d..5c03d33b0c 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
@@ -69,10 +69,5 @@ namespace ImageSharp.Formats.Tiff
/// Size (in bytes) of the Double data type
///
public const int SizeOfDouble = 8;
-
- ///
- /// Size (in bytes) of the word boundary to allign data to when required
- ///
- public const int SizeOfWordBoundary = 4;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 74e8338c20..d32e34c433 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -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;
@@ -50,30 +51,95 @@ namespace ImageSharp.Formats
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);
}
}
///
/// Writes the TIFF file header.
///
- /// The to write data to.
- /// The byte offset to the first IFD in the file.
- public void WriteHeader(BinaryWriter writer, uint firstIfdOffset)
+ /// The to write data to.
+ /// The marker to write the first IFD offset.
+ 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;
+ }
+
+ ///
+ /// Writes a TIFF IFD block.
+ ///
+ /// The to write data to.
+ /// The IFD entries to write to the file.
+ /// The marker to write the next IFD offset (if present).
+ public long WriteIfd(TiffWriter writer, List 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 largeDataBlocks = new List();
+
+ 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;
+ }
+
+ ///
+ /// Writes all data required to define an image
+ ///
+ /// The pixel format.
+ /// The to write data to.
+ /// The to encode from.
+ /// The marker to write this IFD offset.
+ /// The marker to write the next IFD offset (if present).
+ public long WriteImage(TiffWriter writer, Image image, long ifdOffset)
+ where TPixel : struct, IPixel
+ {
+ throw new NotImplementedException();
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
new file mode 100644
index 0000000000..201e7b4da3
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
@@ -0,0 +1,109 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+namespace ImageSharp.Formats.Tiff
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+
+ ///
+ /// Utility class for writing TIFF data to a .
+ ///
+ internal class TiffWriter : IDisposable
+ {
+ private readonly Stream output;
+
+ private readonly byte[] paddingBytes = new byte[4];
+
+ private readonly List references = new List();
+
+ /// Initializes a new instance of the class.
+ /// The output stream.
+ public TiffWriter(Stream output)
+ {
+ this.output = output;
+ }
+
+ ///
+ /// Gets a flag indicating whether the architecture is little-endian.
+ ///
+ public bool IsLittleEndian => BitConverter.IsLittleEndian;
+
+ ///
+ /// Returns the current position within the stream.
+ ///
+ public long Position => this.output.Position;
+
+ /// Writes an empty four bytes to the stream, returning the offset to be written later.
+ /// The offset to be written later
+ public long PlaceMarker()
+ {
+ long offset = this.output.Position;
+ this.Write(0u);
+ return offset;
+ }
+
+ /// Writes an array of bytes to the current stream.
+ /// The bytes to write.
+ public void Write(byte[] value)
+ {
+ this.output.Write(value, 0, value.Length);
+ }
+
+ /// Writes a byte to the current stream.
+ /// The byte to write.
+ public void Write(byte value)
+ {
+ this.output.Write(new byte[] { value }, 0, 1);
+ }
+
+ /// Writes a two-byte unsigned integer to the current stream.
+ /// The two-byte unsigned integer to write.
+ public void Write(ushort value)
+ {
+ byte[] bytes = BitConverter.GetBytes(value);
+ this.output.Write(bytes, 0, 2);
+ }
+
+ /// Writes a four-byte unsigned integer to the current stream.
+ /// The four-byte unsigned integer to write.
+ public void Write(uint value)
+ {
+ byte[] bytes = BitConverter.GetBytes(value);
+ this.output.Write(bytes, 0, 4);
+ }
+
+ /// Writes an array of bytes to the current stream, padded to four-bytes.
+ /// The bytes to write.
+ 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);
+ }
+ }
+
+ /// Writes a four-byte unsigned integer to the specified marker in the stream.
+ /// The offset returned when placing the marker
+ /// The four-byte unsigned integer to write.
+ 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);
+ }
+
+ ///
+ /// Disposes instance, ensuring any unwritten data is flushed.
+ ///
+ public void Dispose()
+ {
+ this.output.Flush();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
index d5c21f5948..76d15f6a10 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
@@ -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
@@ -20,41 +22,24 @@ namespace ImageSharp.Tests
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(() => { 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(() => { 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);
}
}
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs
new file mode 100644
index 0000000000..c4c4fb84bf
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderIfdTests.cs
@@ -0,0 +1,299 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.IO;
+ using System.Linq;
+ using Xunit;
+
+ using ImageSharp.Formats;
+ using ImageSharp.Formats.Tiff;
+ using System.Text;
+ using System.Collections.Generic;
+
+ public class TiffEncoderIfdTests
+ {
+ [Fact]
+ public void WriteIfd_DataIsCorrectLength()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ Assert.Equal(2 + 12 * 3 + 4, stream.Length);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesNumberOfEntries()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntryBytes = stream.ToArray().Take(2).ToArray();
+ Assert.Equal(new byte[] { 3, 0 }, ifdEntryBytes);
+ }
+
+ [Fact]
+ public void WriteIfd_ReturnsNextIfdMarker()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ Assert.Equal(2 + 12 * 3, nextIfdMarker);
+ }
+ }
+
+ [Fact]
+ public void WriteIfd_WritesTagIdForEachEntry()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(10, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(20, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(30, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(2 + 12 * 0).Take(2).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(2 + 12 * 1).Take(2).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(2 + 12 * 2).Take(2).ToArray();
+
+ Assert.Equal(new byte[] { 10, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 20, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 30, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesTypeForEachEntry()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 4, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(4 + 12 * 0).Take(2).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(4 + 12 * 1).Take(2).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(4 + 12 * 2).Take(2).ToArray();
+
+ Assert.Equal(new byte[] { 4, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 3, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 2, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesCountForEachEntry()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 4, new byte[] { (byte)'A', (byte)'B', (byte)'C', 0 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(6 + 12 * 0).Take(4).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(6 + 12 * 1).Take(4).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(6 + 12 * 2).Take(4).ToArray();
+
+ Assert.Equal(new byte[] { 1, 0, 0, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 2, 0, 0, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 4, 0, 0, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesDataInline()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 2, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(10 + 12 * 0).Take(4).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(10 + 12 * 1).Take(4).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(10 + 12 * 2).Take(4).ToArray();
+
+ Assert.Equal(new byte[] { 1, 2, 3, 4 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 5, 6, 7, 8 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesDataByReference()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Byte, 8, new byte[] { 1, 2, 3, 4, 4, 3, 2, 1 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 4, new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write(new byte[] { 1, 2, 3, 4 });
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(14 + 12 * 0).Take(4).ToArray();
+ var ifdEntry1Data = stream.ToArray().Skip(46).Take(8).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(14 + 12 * 1).Take(4).ToArray();
+ var ifdEntry2Data = stream.ToArray().Skip(54).Take(8).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(14 + 12 * 2).Take(4).ToArray();
+
+ Assert.Equal(new byte[] { 46, 0, 0, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 1, 2, 3, 4, 4, 3, 2, 1 }, ifdEntry1Data);
+ Assert.Equal(new byte[] { 54, 0, 0, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }, ifdEntry2Data);
+ Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesDataByReferenceOnWordBoundary()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(TiffTags.ImageWidth, TiffType.Byte, 8, new byte[] { 1, 2, 3, 4, 5 }),
+ new TiffIfdEntry(TiffTags.ImageLength, TiffType.Short, 4, new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }),
+ new TiffIfdEntry(TiffTags.Compression, TiffType.Ascii, 3, new byte[] { (byte)'A', (byte)'B', 0 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write(new byte[] { 1, 2, 3, 4 });
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(14 + 12 * 0).Take(4).ToArray();
+ var ifdEntry1Data = stream.ToArray().Skip(46).Take(5).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(14 + 12 * 1).Take(4).ToArray();
+ var ifdEntry2Data = stream.ToArray().Skip(52).Take(8).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(14 + 12 * 2).Take(4).ToArray();
+
+ Assert.Equal(new byte[] { 46, 0, 0, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, ifdEntry1Data);
+ Assert.Equal(new byte[] { 52, 0, 0, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }, ifdEntry2Data);
+ Assert.Equal(new byte[] { (byte)'A', (byte)'B', 0, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_WritesEntriesInCorrectOrder()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List()
+ {
+ new TiffIfdEntry(10, TiffType.Long, 1, new byte[] { 1, 2, 3, 4 }),
+ new TiffIfdEntry(30, TiffType.Long, 1, new byte[] { 5, 6, 7, 8 }),
+ new TiffIfdEntry(20, TiffType.Long, 1, new byte[] { 9, 10, 11, 12 })
+ };
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ long nextIfdMarker = encoder.WriteIfd(writer, entries);
+ }
+
+ var ifdEntry1Bytes = stream.ToArray().Skip(2 + 12 * 0).Take(2).ToArray();
+ var ifdEntry2Bytes = stream.ToArray().Skip(2 + 12 * 1).Take(2).ToArray();
+ var ifdEntry3Bytes = stream.ToArray().Skip(2 + 12 * 2).Take(2).ToArray();
+
+ Assert.Equal(new byte[] { 10, 0 }, ifdEntry1Bytes);
+ Assert.Equal(new byte[] { 20, 0 }, ifdEntry2Bytes);
+ Assert.Equal(new byte[] { 30, 0 }, ifdEntry3Bytes);
+ }
+
+ [Fact]
+ public void WriteIfd_ThrowsException_IfNoEntriesArePresent()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ List entries = new List();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ ArgumentException e = Assert.Throws(() => { encoder.WriteIfd(writer, entries); });
+
+ Assert.Equal("There must be at least one entry per IFD.\r\nParameter name: entries", e.Message);
+ Assert.Equal("entries", e.ParamName);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs
new file mode 100644
index 0000000000..31582fb6d8
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/Utils/TiffWriterTests.cs
@@ -0,0 +1,135 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.IO;
+ using Xunit;
+
+ using ImageSharp.Formats.Tiff;
+
+ public class TiffWriterTests
+ {
+ [Fact]
+ public void IsLittleEndian_IsTrueOnWindows()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ Assert.True(writer.IsLittleEndian);
+ }
+ }
+
+ [Theory]
+ [InlineData(new byte[] {}, 0)]
+ [InlineData(new byte[] { 42 }, 1)]
+ [InlineData(new byte[] { 1, 2, 3, 4, 5 }, 5)]
+ public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult)
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write(data);
+ Assert.Equal(writer.Position, expectedResult);
+ }
+ }
+
+ [Fact]
+ public void Write_WritesByte()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write((byte)42);
+ }
+
+ Assert.Equal(new byte[] { 42 }, stream.ToArray());
+ }
+
+ [Fact]
+ public void Write_WritesByteArray()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write(new byte[] { 2, 4, 6, 8 });
+ }
+
+ Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray());
+ }
+
+ [Fact]
+ public void Write_WritesUInt16()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write((ushort)1234);
+ }
+
+ Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray());
+ }
+
+ [Fact]
+ public void Write_WritesUInt32()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write((uint)12345678);
+ }
+
+ Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray());
+ }
+
+ [Theory]
+ [InlineData(new byte[] { }, new byte[] { 0, 0, 0, 0 })]
+ [InlineData(new byte[] { 2 }, new byte[] { 2, 0, 0, 0 })]
+ [InlineData(new byte[] { 2, 4 }, new byte[] { 2, 4, 0, 0 })]
+ [InlineData(new byte[] { 2, 4, 6 }, new byte[] { 2, 4, 6, 0 })]
+ [InlineData(new byte[] { 2, 4, 6, 8 }, new byte[] { 2, 4, 6, 8 })]
+ [InlineData(new byte[] { 2, 4, 6, 8, 10, 12 }, new byte[] { 2, 4, 6, 8, 10, 12 })]
+ public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult)
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.WritePadded(bytes);
+ }
+
+ Assert.Equal(expectedResult, stream.ToArray());
+ }
+
+ [Fact]
+ public void WriteMarker_WritesToPlacedPosition()
+ {
+ MemoryStream stream = new MemoryStream();
+
+ using (TiffWriter writer = new TiffWriter(stream))
+ {
+ writer.Write((uint)0x11111111);
+ long marker = writer.PlaceMarker();
+ writer.Write((uint)0x33333333);
+
+ writer.WriteMarker(marker, 0x12345678);
+
+ writer.Write((uint)0x44444444);
+ }
+
+ Assert.Equal(new byte[] { 0x11, 0x11, 0x11, 0x11,
+ 0x78, 0x56, 0x34, 0x12,
+ 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44 }, stream.ToArray());
+ }
+ }
+}
\ No newline at end of file