Skip to content

Commit

Permalink
Merge pull request #1330 from IldarKhayrutdinov/tiff-codec_rebased
Browse files Browse the repository at this point in the history
#12 Tiff codec
  • Loading branch information
JimBobSquarePants authored Aug 28, 2020
2 parents d908477 + a9e3d89 commit 6fe3872
Show file tree
Hide file tree
Showing 169 changed files with 12,673 additions and 89 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
*.tga binary
*.ttc binary
*.ttf binary
*.tif binary
*.tiff binary
*.webp binary
*.woff binary
*.woff2 binary
Expand Down
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 Version 16
VisualStudioVersion = 16.0.28902.138
Expand Down
2 changes: 2 additions & 0 deletions src/ImageSharp/Advanced/AotCompilerTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ private static void Seed<TPixel>()
AotCodec<TPixel>(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder());
AotCodec<TPixel>(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder());
AotCodec<TPixel>(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder());
AotCodec<TPixel>(new Formats.Tga.TgaDecoder(), new Formats.Tga.TgaEncoder());
AotCodec<TPixel>(new Formats.Tiff.TiffDecoder(), new Formats.Tiff.TiffEncoder());

// TODO: Do the discovery work to figure out what works and what doesn't.
}
Expand Down
5 changes: 4 additions & 1 deletion src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Processing;
Expand Down Expand Up @@ -180,6 +181,7 @@ public Configuration Clone()
/// <see cref="GifConfigurationModule"/>
/// <see cref="BmpConfigurationModule"/>.
/// <see cref="TgaConfigurationModule"/>.
/// <see cref="TiffConfigurationModule"/>
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance()
Expand All @@ -189,7 +191,8 @@ internal static Configuration CreateDefaultInstance()
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule(),
new TgaConfigurationModule());
new TgaConfigurationModule(),
new TiffConfigurationModule());
}
}
}
106 changes: 105 additions & 1 deletion src/ImageSharp/Formats/ImageExtensions.Save.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

// <auto-generated />
Expand All @@ -12,6 +12,7 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;

namespace SixLabors.ImageSharp
{
Expand Down Expand Up @@ -535,5 +536,108 @@ public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder e
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance),
cancellationToken);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsTiff(this Image source, string path) => SaveAsTiff(source, path, null);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path) => SaveAsTiffAsync(source, path, null);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsTiffAsync(source, path, null, cancellationToken);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsTiff(this Image source, string path, TiffEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsTiff(this Image source, Stream stream)
=> SaveAsTiff(source, stream, null);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsTiffAsync(source, stream, null, cancellationToken);

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance));

/// <summary>
/// Saves the image to the given stream with the Tiff format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);

}
}
3 changes: 2 additions & 1 deletion src/ImageSharp/Formats/ImageExtensions.Save.tt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<#@ template language="C#" #>
<#@ template language="C#" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
// Copyright (c) Six Labors.
Expand All @@ -17,6 +17,7 @@ using SixLabors.ImageSharp.Advanced;
"Jpeg",
"Png",
"Tga",
"Tiff",
};

foreach (string fmt in formats)
Expand Down
56 changes: 56 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using System.IO.Compression;
using SixLabors.ImageSharp.Memory;

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 class DeflateTiffCompression : TiffBaseCompression
{
public DeflateTiffCompression(MemoryAllocator allocator)
: base(allocator)
{
}

/// <inheritdoc/>
public override void Decompress(Stream stream, int byteCount, Span<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;
var subStream = new SubStream(stream, byteCount - headerLength);
using (var deflateStream = new DeflateStream(subStream, 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.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed using LZW compression.
/// </summary>
internal class LzwTiffCompression : TiffBaseCompression
{
public LzwTiffCompression(MemoryAllocator allocator)
: base(allocator)
{
}

/// <inheritdoc/>
public override void Decompress(Stream stream, int byteCount, Span<byte> buffer)
{
var 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.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is not compressed.
/// </summary>
internal class NoneTiffCompression : TiffBaseCompression
{
public NoneTiffCompression(MemoryAllocator allocator)
: base(allocator)
{
}

/// <inheritdoc/>
public override void Decompress(Stream stream, int byteCount, Span<byte> buffer)
{
stream.ReadFull(buffer, byteCount);
}
}
}
71 changes: 71 additions & 0 deletions src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed using PackBits compression.
/// </summary>
internal class PackBitsTiffCompression : TiffBaseCompression
{
public PackBitsTiffCompression(MemoryAllocator allocator)
: base(allocator)
{
}

/// <inheritdoc/>
public override void Decompress(Stream stream, int byteCount, Span<byte> buffer)
{
using IMemoryOwner<byte> compressedDataMemory = this.Allocator.Allocate<byte>(byteCount);

Span<byte> compressedData = compressedDataMemory.GetSpan();

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;

compressedData.Slice(literalOffset, literalLength).CopyTo(buffer.Slice(decompressedOffset));

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;
}
}
}

private static void ArrayCopyRepeat(byte value, Span<byte> destinationArray, int destinationIndex, int length)
{
for (int i = 0; i < length; i++)
{
destinationArray[i + destinationIndex] = value;
}
}
}
}
Loading

0 comments on commit 6fe3872

Please sign in to comment.