Browse Source

Add TiffEncoder and write TIFF header

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
fd0f49f050
  1. 15
      src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
  2. 3
      src/ImageSharp/Formats/Tiff/TiffEncoder.cs
  3. 79
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  4. 61
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs

15
src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs

@ -20,6 +20,16 @@ namespace ImageSharp.Formats.Tiff
/// </summary>
public const byte ByteOrderBigEndian = 0x4D;
/// <summary>
/// Byte order markers for indicating little endian encoding.
/// </summary>
public const ushort ByteOrderLittleEndianShort = 0x4949;
/// <summary>
/// Byte order markers for indicating big endian encoding.
/// </summary>
public const ushort ByteOrderBigEndianShort = 0x4D4D;
/// <summary>
/// Magic number used within the image file header to identify a TIFF format file.
/// </summary>
@ -59,5 +69,10 @@ namespace ImageSharp.Formats.Tiff
/// 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;
}
}

3
src/ImageSharp/Formats/Tiff/TiffEncoder.cs

@ -33,7 +33,8 @@ namespace ImageSharp.Formats
public void Encode<TPixel>(Image<TPixel> image, Stream stream, ITiffEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
throw new NotImplementedException();
var encode = new TiffEncoderCore(options);
encode.Encode(image, stream);
}
}
}

79
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -0,0 +1,79 @@
// <copyright file="TiffEncoderCore.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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;
/// <summary>
/// Performs the TIFF encoding operation.
/// </summary>
internal sealed class TiffEncoderCore
{
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly ITiffEncoderOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="TiffEncoderCore"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
public TiffEncoderCore(ITiffEncoderOptions options)
{
this.options = options ?? new TiffEncoderOptions();
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
{
this.WriteHeader(writer, 0);
}
}
/// <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)
{
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);
}
}
}

61
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs

@ -0,0 +1,61 @@
// <copyright file="TiffEncoderHeaderTests.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 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<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);
}
}
}
}
Loading…
Cancel
Save