Skip to content

Commit

Permalink
Merge pull request #119 from Andy-Wilkinson/tiff-codec
Browse files Browse the repository at this point in the history
[WIP] Implement Tiff codec
  • Loading branch information
JimBobSquarePants committed Dec 10, 2018
2 parents 700b07e + 879051a commit c46fc03
Show file tree
Hide file tree
Showing 102 changed files with 14,915 additions and 4,105 deletions.
1 change: 0 additions & 1 deletion ImageSharp.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.12
Expand Down
57 changes: 57 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed using Deflate compression.
/// </summary>
/// <remarks>
/// Note that the 'OldDeflate' compression type is identical to the 'Deflate' compression type.
/// </remarks>
internal static class DeflateTiffCompression
{
/// <summary>
/// Decompresses image data into the supplied buffer.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to read image data from.</param>
/// <param name="byteCount">The number of bytes to read from the input stream.</param>
/// <param name="buffer">The output buffer for uncompressed data.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decompress(Stream stream, int byteCount, byte[] buffer)
{
// Read the 'zlib' header information
int cmf = stream.ReadByte();
int flag = stream.ReadByte();

if ((cmf & 0x0f) != 8)
{
throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}");
}

// If the 'fdict' flag is set then we should skip the next four bytes
bool fdict = (flag & 32) != 0;

if (fdict)
{
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
}

// The subsequent data is the Deflate compressed data (except for the last four bytes of checksum)
int headerLength = fdict ? 10 : 6;
SubStream subStream = new SubStream(stream, byteCount - headerLength);
using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress, true))
{
deflateStream.ReadFull(buffer);
}
}
}
}
30 changes: 30 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed using LZW compression.
/// </summary>
internal static class LzwTiffCompression
{
/// <summary>
/// Decompresses image data into the supplied buffer.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to read image data from.</param>
/// <param name="byteCount">The number of bytes to read from the input stream.</param>
/// <param name="buffer">The output buffer for uncompressed data.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decompress(Stream stream, int byteCount, byte[] buffer)
{
SubStream subStream = new SubStream(stream, byteCount);
using (var decoder = new TiffLzwDecoder(subStream))
{
decoder.DecodePixels(buffer.Length, 8, buffer);
}
}
}
}
26 changes: 26 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System.IO;
using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is not compressed.
/// </summary>
internal static class NoneTiffCompression
{
/// <summary>
/// Decompresses image data into the supplied buffer.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to read image data from.</param>
/// <param name="byteCount">The number of bytes to read from the input stream.</param>
/// <param name="buffer">The output buffer for uncompressed data.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decompress(Stream stream, int byteCount, byte[] buffer)
{
stream.ReadFull(buffer, byteCount);
}
}
}
77 changes: 77 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed using PackBits compression.
/// </summary>
internal static class PackBitsTiffCompression
{
/// <summary>
/// Decompresses image data into the supplied buffer.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to read image data from.</param>
/// <param name="byteCount">The number of bytes to read from the input stream.</param>
/// <param name="buffer">The output buffer for uncompressed data.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decompress(Stream stream, int byteCount, byte[] buffer)
{
byte[] compressedData = ArrayPool<byte>.Shared.Rent(byteCount);

try
{
stream.ReadFull(compressedData, byteCount);
int compressedOffset = 0;
int decompressedOffset = 0;

while (compressedOffset < byteCount)
{
byte headerByte = compressedData[compressedOffset];

if (headerByte <= (byte)127)
{
int literalOffset = compressedOffset + 1;
int literalLength = compressedData[compressedOffset] + 1;

Array.Copy(compressedData, literalOffset, buffer, decompressedOffset, literalLength);

compressedOffset += literalLength + 1;
decompressedOffset += literalLength;
}
else if (headerByte == (byte)0x80)
{
compressedOffset += 1;
}
else
{
byte repeatData = compressedData[compressedOffset + 1];
int repeatLength = 257 - headerByte;

ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength);

compressedOffset += 2;
decompressedOffset += repeatLength;
}
}
}
finally
{
ArrayPool<byte>.Shared.Return(compressedData);
}
}

private static void ArrayCopyRepeat(byte value, byte[] destinationArray, int destinationIndex, int length)
{
for (int i = 0; i < length; i++)
{
destinationArray[i + destinationIndex] = value;
}
}
}
}
31 changes: 31 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/TiffCompressionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Provides enumeration of the various TIFF compression types.
/// </summary>
internal enum TiffCompressionType
{
/// <summary>
/// Image data is stored uncompressed in the TIFF file.
/// </summary>
None = 0,

/// <summary>
/// Image data is compressed using PackBits compression.
/// </summary>
PackBits = 1,

/// <summary>
/// Image data is compressed using Deflate compression.
/// </summary>
Deflate = 2,

/// <summary>
/// Image data is compressed using LZW compression.
/// </summary>
Lzw = 3,
}
}
71 changes: 71 additions & 0 deletions src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Enumeration representing the compression formats defined by the Tiff file-format.
/// </summary>
internal enum TiffCompression
{
/// <summary>
/// No compression.
/// </summary>
None = 1,

/// <summary>
/// CCITT Group 3 1-Dimensional Modified Huffman run-length encoding.
/// </summary>
Ccitt1D = 2,

/// <summary>
/// PackBits compression
/// </summary>
PackBits = 32773,

/// <summary>
/// T4-encoding: CCITT T.4 bi-level encoding (see Section 11 of the TIFF 6.0 specification).
/// </summary>
CcittGroup3Fax = 3,

/// <summary>
/// T6-encoding: CCITT T.6 bi-level encoding (see Section 11 of the TIFF 6.0 specification).
/// </summary>
CcittGroup4Fax = 4,

/// <summary>
/// LZW compression (see Section 13 of the TIFF 6.0 specification).
/// </summary>
Lzw = 5,

/// <summary>
/// JPEG compression - obsolete (see Section 22 of the TIFF 6.0 specification).
/// </summary>
OldJpeg = 6,

/// <summary>
/// JPEG compression (see TIFF Specification, supplement 2).
/// </summary>
Jpeg = 7,

/// <summary>
/// Deflate compression, using zlib data format (see TIFF Specification, supplement 2).
/// </summary>
Deflate = 8,

/// <summary>
/// Deflate compression - old.
/// </summary>
OldDeflate = 32946,

/// <summary>
/// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301).
/// </summary>
ItuTRecT82 = 9,

/// <summary>
/// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
/// </summary>
ItuTRecT43 = 10
}
}
83 changes: 83 additions & 0 deletions src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System.Collections.Generic;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Defines constants defined in the TIFF specification.
/// </summary>
internal static class TiffConstants
{
/// <summary>
/// Byte order markers for indicating little endian encoding.
/// </summary>
public const byte ByteOrderLittleEndian = 0x49;

/// <summary>
/// Byte order markers for indicating big endian encoding.
/// </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>
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;

/// <summary>
/// Size (in bytes) of the Short and SShort data types
/// </summary>
public const int SizeOfShort = 2;

/// <summary>
/// Size (in bytes) of the Long and SLong data types
/// </summary>
public const int SizeOfLong = 4;

/// <summary>
/// Size (in bytes) of the Rational and SRational data types
/// </summary>
public const int SizeOfRational = 8;

/// <summary>
/// Size (in bytes) of the Float data type
/// </summary>
public const int SizeOfFloat = 4;

/// <summary>
/// Size (in bytes) of the Double data type
/// </summary>
public const int SizeOfDouble = 8;

/// <summary>
/// The list of mimetypes that equate to a tiff.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/tiff", "image/tiff-fx" };

/// <summary>
/// The list of file extensions that equate to a tiff.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "tiff", "tif" };
}
}
Loading

0 comments on commit c46fc03

Please sign in to comment.