Skip to content

Commit 5d894d4

Browse files
committed
Add support for decoding Tiff images with associated alpha data
1 parent 823e785 commit 5d894d4

32 files changed

+362
-74
lines changed

src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ public enum TiffCompression : ushort
2323
/// </summary>
2424
Ccitt1D = 2,
2525

26-
/// <summary>
27-
/// PackBits compression
28-
/// </summary>
29-
PackBits = 32773,
30-
3126
/// <summary>
3227
/// T4-encoding: CCITT T.4 bi-level encoding (see Section 11 of the TIFF 6.0 specification).
3328
/// </summary>
@@ -65,27 +60,48 @@ public enum TiffCompression : ushort
6560
Deflate = 8,
6661

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

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

8378
/// <summary>
84-
/// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
79+
/// NeXT 2-bit Grey Scale compression algorithm.
8580
///
86-
/// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
81+
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
82+
/// if this is chosen.
83+
/// </summary>
84+
NeXT = 32766,
85+
86+
/// <summary>
87+
/// PackBits compression.
88+
/// </summary>
89+
PackBits = 32773,
90+
91+
/// <summary>
92+
/// ThunderScan 4-bit compression.
93+
///
94+
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
8795
/// if this is chosen.
8896
/// </summary>
89-
ItuTRecT43 = 10
97+
ThunderScan = 32809,
98+
99+
/// <summary>
100+
/// Deflate compression - old.
101+
///
102+
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
103+
/// if this is chosen.
104+
/// </summary>
105+
OldDeflate = 32946,
90106
}
91107
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
5555
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
5656
offset += 2;
5757

58-
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
58+
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
5959
}
6060
}
6161
else

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
5050

5151
offset += 2;
5252

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

6464
offset += 2;
6565

66-
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
66+
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
6767
}
6868
}
6969
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.Numerics;
56
using SixLabors.ImageSharp.Formats.Tiff.Utils;
67
using SixLabors.ImageSharp.Memory;
78
using SixLabors.ImageSharp.PixelFormats;
@@ -18,15 +19,23 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
1819

1920
private readonly Configuration configuration;
2021

22+
private readonly MemoryAllocator memoryAllocator;
23+
24+
private readonly TiffExtraSampleType? extraSamplesType;
25+
2126
/// <summary>
2227
/// Initializes a new instance of the <see cref="Rgba16161616TiffColor{TPixel}" /> class.
2328
/// </summary>
2429
/// <param name="configuration">The configuration.</param>
30+
/// <param name="memoryAllocator">The memory allocator.</param>
2531
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
26-
public Rgba16161616TiffColor(Configuration configuration, bool isBigEndian)
32+
/// <param name="extraSamplesType">The type of the extra samples.</param>
33+
public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian)
2734
{
2835
this.configuration = configuration;
2936
this.isBigEndian = isBigEndian;
37+
this.memoryAllocator = memoryAllocator;
38+
this.extraSamplesType = extraSamplesType;
3039
}
3140

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

50+
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
4151
int offset = 0;
4252

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

60-
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
72+
pixelRow[x] = hasAssociatedAlpha ?
73+
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
74+
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
6175
}
6276
}
6377
else
@@ -69,6 +83,12 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
6983
pixelRow,
7084
pixelRow.Length);
7185

86+
if (hasAssociatedAlpha)
87+
{
88+
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
89+
TiffUtils.UnPremultiplyRow(vectorsSpan, pixelRow, color);
90+
}
91+
7292
offset += byteCount;
7393
}
7494
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,18 @@ internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
1717
{
1818
private readonly bool isBigEndian;
1919

20+
private readonly TiffExtraSampleType? extraSamplesType;
21+
2022
/// <summary>
2123
/// Initializes a new instance of the <see cref="Rgba16PlanarTiffColor{TPixel}" /> class.
2224
/// </summary>
23-
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
24-
public Rgba16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
25+
/// <param name="extraSamplesType">The extra samples type.</param>
26+
/// <param name="isBigEndian">If set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
27+
public Rgba16PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
28+
{
29+
this.extraSamplesType = extraSamplesType;
30+
this.isBigEndian = isBigEndian;
31+
}
2532

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

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

5361
offset += 2;
5462

55-
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
63+
pixelRow[x] = hasAssociatedAlpha ?
64+
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
65+
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
5666
}
5767
}
5868
else
@@ -62,11 +72,13 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
6272
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
6373
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
6474
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
65-
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
75+
ulong a = TiffUtils.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2));
6676

6777
offset += 2;
6878

69-
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
79+
pixelRow[x] = hasAssociatedAlpha ?
80+
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
81+
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
7082
}
7183
}
7284
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@ internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
1616
{
1717
private readonly bool isBigEndian;
1818

19+
private readonly TiffExtraSampleType? extraSamplesType;
20+
1921
/// <summary>
2022
/// Initializes a new instance of the <see cref="Rgba24242424TiffColor{TPixel}" /> class.
2123
/// </summary>
24+
/// <param name="extraSamplesType">The type of the extra samples.</param>
2225
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
23-
public Rgba24242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
26+
public Rgba24242424TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
27+
{
28+
this.extraSamplesType = extraSamplesType;
29+
this.isBigEndian = isBigEndian;
30+
}
2431

2532
/// <inheritdoc/>
2633
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
@@ -29,7 +36,10 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
2936
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
3037
var color = default(TPixel);
3138
color.FromVector4(TiffUtils.Vector4Default);
39+
40+
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
3241
int offset = 0;
42+
3343
Span<byte> buffer = stackalloc byte[4];
3444
int bufferStartIdx = this.isBigEndian ? 1 : 0;
3545

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

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

84-
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
96+
pixelRow[x] = hasAssociatedAlpha ?
97+
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
98+
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
8599
}
86100
}
87101
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,18 @@ internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
1717
{
1818
private readonly bool isBigEndian;
1919

20+
private readonly TiffExtraSampleType? extraSamplesType;
21+
2022
/// <summary>
2123
/// Initializes a new instance of the <see cref="Rgba24PlanarTiffColor{TPixel}" /> class.
2224
/// </summary>
25+
/// <param name="extraSamplesType">The extra samples type.</param>
2326
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
24-
public Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
27+
public Rgba24PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
28+
{
29+
this.extraSamplesType = extraSamplesType;
30+
this.isBigEndian = isBigEndian;
31+
}
2532

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

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

5967
offset += 3;
6068

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

7787
offset += 3;
7888

79-
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
89+
pixelRow[x] = hasAssociatedAlpha ?
90+
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
91+
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
8092
}
8193
}
8294
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@ internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
1616
{
1717
private readonly bool isBigEndian;
1818

19+
private readonly TiffExtraSampleType? extraSamplesType;
20+
1921
/// <summary>
2022
/// Initializes a new instance of the <see cref="Rgba32323232TiffColor{TPixel}" /> class.
2123
/// </summary>
24+
/// <param name="extraSamplesType">The type of the extra samples.</param>
2225
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
23-
public Rgba32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
26+
public Rgba32323232TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
27+
{
28+
this.extraSamplesType = extraSamplesType;
29+
this.isBigEndian = isBigEndian;
30+
}
2431

2532
/// <inheritdoc/>
2633
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
@@ -29,6 +36,8 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
2936
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
3037
var color = default(TPixel);
3138
color.FromVector4(TiffUtils.Vector4Default);
39+
40+
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
3241
int offset = 0;
3342

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

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

73-
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
84+
pixelRow[x] = hasAssociatedAlpha ?
85+
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
86+
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
7487
}
7588
}
7689
}

0 commit comments

Comments
 (0)