Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for decoding Tiff images with associated alpha data #2062

Merged
merged 8 commits into from
Mar 15, 2022
42 changes: 29 additions & 13 deletions src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ public enum TiffCompression : ushort
/// </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>
Expand Down Expand Up @@ -65,27 +60,48 @@ public enum TiffCompression : ushort
Deflate = 8,

/// <summary>
/// Deflate compression - old.
/// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301).
///
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
OldDeflate = 32946,
ItuTRecT82 = 9,

/// <summary>
/// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301).
/// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
///
/// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
ItuTRecT82 = 9,
ItuTRecT43 = 10,

/// <summary>
/// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
/// NeXT 2-bit Grey Scale compression algorithm.
///
/// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
NeXT = 32766,

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

/// <summary>
/// ThunderScan 4-bit compression.
///
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
ItuTRecT43 = 10
ThunderScan = 32809,

/// <summary>
/// Deflate compression - old.
///
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
OldDeflate = 32946,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,

offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
else
Expand All @@ -63,7 +63,7 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,

offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
Expand All @@ -18,15 +19,23 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>

private readonly Configuration configuration;

private readonly MemoryAllocator memoryAllocator;

private readonly TiffExtraSampleType? extraSamplesType;

/// <summary>
/// Initializes a new instance of the <see cref="Rgba16161616TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba16161616TiffColor(Configuration configuration, bool isBigEndian)
/// <param name="extraSamplesType">The type of the extra samples.</param>
public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.configuration = configuration;
this.isBigEndian = isBigEndian;
this.memoryAllocator = memoryAllocator;
this.extraSamplesType = extraSamplesType;
}

/// <inheritdoc/>
Expand All @@ -38,8 +47,11 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);

bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;

using System.Buffers.IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null;
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span<Vector4>.Empty;
for (int y = top; y < top + height; y++)
{
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
Expand All @@ -57,7 +69,9 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
else
Expand All @@ -69,6 +83,12 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
pixelRow,
pixelRow.Length);

if (hasAssociatedAlpha)
{
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
JimBobSquarePants marked this conversation as resolved.
Show resolved Hide resolved
TiffUtils.UnPremultiplyRow(vectorsSpan, pixelRow, color);
}

offset += byteCount;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
{
private readonly bool isBigEndian;

private readonly TiffExtraSampleType? extraSamplesType;

/// <summary>
/// Initializes a new instance of the <see cref="Rgba16PlanarTiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <param name="extraSamplesType">The extra samples type.</param>
/// <param name="isBigEndian">If set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba16PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.extraSamplesType = extraSamplesType;
this.isBigEndian = isBigEndian;
}

/// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
Expand All @@ -37,6 +44,7 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
Span<byte> blueData = data[2].GetSpan();
Span<byte> alphaData = data[3].GetSpan();

bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
{
Expand All @@ -52,7 +60,9 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,

offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
else
Expand All @@ -62,11 +72,13 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
ulong a = TiffUtils.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2));

offset += 2;

pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
private readonly bool isBigEndian;

private readonly TiffExtraSampleType? extraSamplesType;

/// <summary>
/// Initializes a new instance of the <see cref="Rgba24242424TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="extraSamplesType">The type of the extra samples.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba24242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
public Rgba24242424TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.extraSamplesType = extraSamplesType;
this.isBigEndian = isBigEndian;
}

/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
Expand All @@ -29,7 +36,10 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);

bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;

Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;

Expand Down Expand Up @@ -58,7 +68,9 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
offset += 3;

pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
else
Expand All @@ -81,7 +93,9 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
offset += 3;

pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
{
private readonly bool isBigEndian;

private readonly TiffExtraSampleType? extraSamplesType;

/// <summary>
/// Initializes a new instance of the <see cref="Rgba24PlanarTiffColor{TPixel}" /> class.
/// </summary>
/// <param name="extraSamplesType">The extra samples type.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
public Rgba24PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.extraSamplesType = extraSamplesType;
this.isBigEndian = isBigEndian;
}

/// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
Expand All @@ -39,6 +46,7 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
Span<byte> alphaData = data[3].GetSpan();
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx);

bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
{
Expand All @@ -58,7 +66,9 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,

offset += 3;

pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
else
Expand All @@ -76,7 +86,9 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,

offset += 3;

pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
private readonly bool isBigEndian;

private readonly TiffExtraSampleType? extraSamplesType;

/// <summary>
/// Initializes a new instance of the <see cref="Rgba32323232TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="extraSamplesType">The type of the extra samples.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
public Rgba32323232TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.extraSamplesType = extraSamplesType;
this.isBigEndian = isBigEndian;
}

/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
Expand All @@ -29,6 +36,8 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);

bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;

for (int y = top; y < top + height; y++)
Expand All @@ -51,7 +60,9 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4;

pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
else
Expand All @@ -70,7 +81,9 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4;

pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
pixelRow[x] = hasAssociatedAlpha ?
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
}
Expand Down
Loading