diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
index 1858d49b8..10a3478c0 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
@@ -20,6 +20,16 @@ namespace ImageSharp.Formats.Tiff
///
public const byte ByteOrderBigEndian = 0x4D;
+ ///
+ /// Byte order markers for indicating little endian encoding.
+ ///
+ public const ushort ByteOrderLittleEndianShort = 0x4949;
+
+ ///
+ /// Byte order markers for indicating big endian encoding.
+ ///
+ public const ushort ByteOrderBigEndianShort = 0x4D4D;
+
///
/// Magic number used within the image file header to identify a TIFF format file.
///
@@ -59,5 +69,10 @@ 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/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
index 7bcb575db..75ff7dcd4 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
@@ -33,7 +33,8 @@ namespace ImageSharp.Formats
public void Encode(Image image, Stream stream, ITiffEncoderOptions options)
where TPixel : struct, IPixel
{
- throw new NotImplementedException();
+ var encode = new TiffEncoderCore(options);
+ encode.Encode(image, stream);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
new file mode 100644
index 000000000..74e8338c2
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -0,0 +1,79 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System;
+ using System.Buffers;
+ using System.IO;
+ using System.Linq;
+ using System.Runtime.CompilerServices;
+ using System.Text;
+ using ImageSharp.Formats.Tiff;
+ using ImageSharp.Memory;
+ using ImageSharp.PixelFormats;
+
+ using Quantizers;
+
+ using static ComparableExtensions;
+
+ ///
+ /// Performs the TIFF encoding operation.
+ ///
+ internal sealed class TiffEncoderCore
+ {
+ ///
+ /// The options for the encoder.
+ ///
+ private readonly ITiffEncoderOptions options;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ public TiffEncoderCore(ITiffEncoderOptions options)
+ {
+ this.options = options ?? new TiffEncoderOptions();
+ }
+
+ ///
+ /// Encodes the image to the specified stream from the .
+ ///
+ /// The pixel format.
+ /// The to encode from.
+ /// The to encode the image data to.
+ public void Encode(Image image, Stream stream)
+ where TPixel : struct, IPixel
+ {
+ Guard.NotNull(image, nameof(image));
+ Guard.NotNull(stream, nameof(stream));
+
+ using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
+ {
+ this.WriteHeader(writer, 0);
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ 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);
+ }
+ }
+}
\ 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
new file mode 100644
index 000000000..d5c21f594
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
@@ -0,0 +1,61 @@
+//
+// 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;
+ using System.Text;
+
+ public class TiffEncoderHeaderTests
+ {
+ [Fact]
+ public void WriteHeader_WritesValidHeader()
+ {
+ MemoryStream stream = new MemoryStream();
+ TiffEncoderCore encoder = new TiffEncoderCore(null);
+
+ using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
+ {
+ encoder.WriteHeader(writer, 1232);
+ }
+
+ stream.Position = 0;
+ Assert.Equal(8, stream.Length);
+ Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0xD0, 0x04, 0x00, 0x00 }, stream.ToArray());
+ }
+
+ [Fact]
+ public void WriteHeader_ThrowsExceptionIfFirstIfdOffsetIsZero()
+ {
+ 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, 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);
+ }
+ }
+ }
+}
\ No newline at end of file