Skip to content

Commit 8b2ebc5

Browse files
Merge pull request #2037 from SixLabors/bp/extrasamples
Add support for decoding Tiff images with UnassociatedAlphaData
2 parents 913ce38 + 815aee2 commit 8b2ebc5

File tree

51 files changed

+1463
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1463
-45
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
3030
var color = default(TPixel);
3131
color.FromVector4(TiffUtils.Vector4Default);
3232
int offset = 0;
33-
byte[] buffer = new byte[4];
33+
Span<byte> buffer = stackalloc byte[4];
3434
int bufferStartIdx = this.isBigEndian ? 1 : 0;
3535

36-
Span<byte> bufferSpan = buffer.AsSpan(bufferStartIdx);
36+
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx);
3737
for (int y = top; y < top + height; y++)
3838
{
3939
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels,
3030
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
3131
var color = default(TPixel);
3232
color.FromVector4(TiffUtils.Vector4Default);
33-
byte[] buffer = new byte[4];
33+
Span<byte> buffer = stackalloc byte[4];
3434
int bufferStartIdx = this.isBigEndian ? 1 : 0;
3535

3636
Span<byte> redData = data[0].GetSpan();
3737
Span<byte> greenData = data[1].GetSpan();
3838
Span<byte> blueData = data[2].GetSpan();
39-
Span<byte> bufferSpan = buffer.AsSpan(bufferStartIdx);
39+
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx);
4040

4141
int offset = 0;
4242
for (int y = top; y < top + height; y++)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
6+
using SixLabors.ImageSharp.Memory;
7+
using SixLabors.ImageSharp.PixelFormats;
8+
9+
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
10+
{
11+
/// <summary>
12+
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel.
13+
/// </summary>
14+
internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
15+
where TPixel : unmanaged, IPixel<TPixel>
16+
{
17+
private readonly bool isBigEndian;
18+
19+
private readonly Configuration configuration;
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="Rgba16161616TiffColor{TPixel}" /> class.
23+
/// </summary>
24+
/// <param name="configuration">The configuration.</param>
25+
/// <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)
27+
{
28+
this.configuration = configuration;
29+
this.isBigEndian = isBigEndian;
30+
}
31+
32+
/// <inheritdoc/>
33+
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
34+
{
35+
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
36+
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
37+
Rgba64 rgba = TiffUtils.Rgba64Default;
38+
var color = default(TPixel);
39+
color.FromVector4(TiffUtils.Vector4Default);
40+
41+
int offset = 0;
42+
43+
for (int y = top; y < top + height; y++)
44+
{
45+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
46+
47+
if (this.isBigEndian)
48+
{
49+
for (int x = 0; x < pixelRow.Length; x++)
50+
{
51+
ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
52+
offset += 2;
53+
ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
54+
offset += 2;
55+
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
56+
offset += 2;
57+
ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
58+
offset += 2;
59+
60+
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
61+
}
62+
}
63+
else
64+
{
65+
int byteCount = pixelRow.Length * 8;
66+
PixelOperations<TPixel>.Instance.FromRgba64Bytes(
67+
this.configuration,
68+
data.Slice(offset, byteCount),
69+
pixelRow,
70+
pixelRow.Length);
71+
72+
offset += byteCount;
73+
}
74+
}
75+
}
76+
}
77+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Buffers;
6+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
7+
using SixLabors.ImageSharp.Memory;
8+
using SixLabors.ImageSharp.PixelFormats;
9+
10+
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
11+
{
12+
/// <summary>
13+
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit.
14+
/// </summary>
15+
internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
16+
where TPixel : unmanaged, IPixel<TPixel>
17+
{
18+
private readonly bool isBigEndian;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="Rgba16PlanarTiffColor{TPixel}" /> class.
22+
/// </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+
26+
/// <inheritdoc/>
27+
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
28+
{
29+
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
30+
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
31+
Rgba64 rgba = TiffUtils.Rgba64Default;
32+
var color = default(TPixel);
33+
color.FromVector4(TiffUtils.Vector4Default);
34+
35+
Span<byte> redData = data[0].GetSpan();
36+
Span<byte> greenData = data[1].GetSpan();
37+
Span<byte> blueData = data[2].GetSpan();
38+
Span<byte> alphaData = data[3].GetSpan();
39+
40+
int offset = 0;
41+
for (int y = top; y < top + height; y++)
42+
{
43+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
44+
if (this.isBigEndian)
45+
{
46+
for (int x = 0; x < pixelRow.Length; x++)
47+
{
48+
ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2));
49+
ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2));
50+
ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2));
51+
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
52+
53+
offset += 2;
54+
55+
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
56+
}
57+
}
58+
else
59+
{
60+
for (int x = 0; x < pixelRow.Length; x++)
61+
{
62+
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
63+
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
64+
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
65+
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
66+
67+
offset += 2;
68+
69+
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
6+
using SixLabors.ImageSharp.Memory;
7+
using SixLabors.ImageSharp.PixelFormats;
8+
9+
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
10+
{
11+
/// <summary>
12+
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel.
13+
/// </summary>
14+
internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
15+
where TPixel : unmanaged, IPixel<TPixel>
16+
{
17+
private readonly bool isBigEndian;
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="Rgba24242424TiffColor{TPixel}" /> class.
21+
/// </summary>
22+
/// <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;
24+
25+
/// <inheritdoc/>
26+
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
27+
{
28+
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
29+
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
30+
var color = default(TPixel);
31+
color.FromVector4(TiffUtils.Vector4Default);
32+
int offset = 0;
33+
Span<byte> buffer = stackalloc byte[4];
34+
int bufferStartIdx = this.isBigEndian ? 1 : 0;
35+
36+
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx);
37+
for (int y = top; y < top + height; y++)
38+
{
39+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
40+
41+
if (this.isBigEndian)
42+
{
43+
for (int x = 0; x < pixelRow.Length; x++)
44+
{
45+
data.Slice(offset, 3).CopyTo(bufferSpan);
46+
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer);
47+
offset += 3;
48+
49+
data.Slice(offset, 3).CopyTo(bufferSpan);
50+
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer);
51+
offset += 3;
52+
53+
data.Slice(offset, 3).CopyTo(bufferSpan);
54+
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer);
55+
offset += 3;
56+
57+
data.Slice(offset, 3).CopyTo(bufferSpan);
58+
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
59+
offset += 3;
60+
61+
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
62+
}
63+
}
64+
else
65+
{
66+
for (int x = 0; x < pixelRow.Length; x++)
67+
{
68+
data.Slice(offset, 3).CopyTo(bufferSpan);
69+
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer);
70+
offset += 3;
71+
72+
data.Slice(offset, 3).CopyTo(bufferSpan);
73+
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer);
74+
offset += 3;
75+
76+
data.Slice(offset, 3).CopyTo(bufferSpan);
77+
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer);
78+
offset += 3;
79+
80+
data.Slice(offset, 3).CopyTo(bufferSpan);
81+
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
82+
offset += 3;
83+
84+
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Buffers;
6+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
7+
using SixLabors.ImageSharp.Memory;
8+
using SixLabors.ImageSharp.PixelFormats;
9+
10+
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
11+
{
12+
/// <summary>
13+
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit.
14+
/// </summary>
15+
internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
16+
where TPixel : unmanaged, IPixel<TPixel>
17+
{
18+
private readonly bool isBigEndian;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="Rgba24PlanarTiffColor{TPixel}" /> class.
22+
/// </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 Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
25+
26+
/// <inheritdoc/>
27+
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
28+
{
29+
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
30+
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
31+
var color = default(TPixel);
32+
color.FromVector4(TiffUtils.Vector4Default);
33+
Span<byte> buffer = stackalloc byte[4];
34+
int bufferStartIdx = this.isBigEndian ? 1 : 0;
35+
36+
Span<byte> redData = data[0].GetSpan();
37+
Span<byte> greenData = data[1].GetSpan();
38+
Span<byte> blueData = data[2].GetSpan();
39+
Span<byte> alphaData = data[3].GetSpan();
40+
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx);
41+
42+
int offset = 0;
43+
for (int y = top; y < top + height; y++)
44+
{
45+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
46+
if (this.isBigEndian)
47+
{
48+
for (int x = 0; x < pixelRow.Length; x++)
49+
{
50+
redData.Slice(offset, 3).CopyTo(bufferSpan);
51+
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer);
52+
greenData.Slice(offset, 3).CopyTo(bufferSpan);
53+
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer);
54+
blueData.Slice(offset, 3).CopyTo(bufferSpan);
55+
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer);
56+
alphaData.Slice(offset, 3).CopyTo(bufferSpan);
57+
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
58+
59+
offset += 3;
60+
61+
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
62+
}
63+
}
64+
else
65+
{
66+
for (int x = 0; x < pixelRow.Length; x++)
67+
{
68+
redData.Slice(offset, 3).CopyTo(bufferSpan);
69+
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer);
70+
greenData.Slice(offset, 3).CopyTo(bufferSpan);
71+
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer);
72+
blueData.Slice(offset, 3).CopyTo(bufferSpan);
73+
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer);
74+
alphaData.Slice(offset, 3).CopyTo(bufferSpan);
75+
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
76+
77+
offset += 3;
78+
79+
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
80+
}
81+
}
82+
}
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)