Skip to content

Commit bca2fbc

Browse files
Merge pull request #3032 from Socolin/fix-3031
Fix decoding tiff image with BigEndian + 64 bit / pixel + associated alpha
2 parents 672d788 + c66833c commit bca2fbc

11 files changed

+126
-29
lines changed

.github/workflows/build-and-test.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,6 @@ jobs:
6969
sdk-preview: true
7070
runtime: -x64
7171
codecov: false
72-
- os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable
73-
framework: net10.0
74-
sdk: 10.0.x
75-
sdk-preview: true
76-
runtime: -x64
77-
codecov: false
7872
- os: macos-26
7973
framework: net10.0
8074
sdk: 10.0.x
@@ -99,11 +93,6 @@ jobs:
9993
sdk: 8.0.x
10094
runtime: -x64
10195
codecov: false
102-
- os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable
103-
framework: net8.0
104-
sdk: 8.0.x
105-
runtime: -x64
106-
codecov: false
10796
- os: macos-26
10897
framework: net8.0
10998
sdk: 8.0.x

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

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
3+
34
#nullable disable
45

56
using System.Buffers;
@@ -48,31 +49,53 @@ public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, in
4849

4950
using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null;
5051
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : [];
51-
for (int y = top; y < top + height; y++)
52-
{
53-
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
5452

55-
if (this.isBigEndian)
53+
if (this.isBigEndian)
54+
{
55+
if (hasAssociatedAlpha)
5656
{
57-
for (int x = 0; x < pixelRow.Length; x++)
57+
for (int y = top; y < top + height; y++)
5858
{
59-
ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
60-
offset += 2;
61-
ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
62-
offset += 2;
63-
ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
64-
offset += 2;
65-
ushort a = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
66-
offset += 2;
67-
68-
pixelRow[x] = hasAssociatedAlpha
69-
? TiffUtilities.ColorFromRgba64Premultiplied<TPixel>(r, g, b, a)
70-
: TPixel.FromRgba64(new Rgba64(r, g, b, a));
59+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
60+
61+
for (int x = 0; x < pixelRow.Length; x++)
62+
{
63+
ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
64+
ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 2, 2));
65+
ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 4, 2));
66+
ushort a = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 6, 2));
67+
offset += 8;
68+
69+
pixelRow[x] = TiffUtilities.ColorFromRgba64Premultiplied<TPixel>(r, g, b, a);
70+
}
7171
}
7272
}
7373
else
7474
{
75+
for (int y = top; y < top + height; y++)
76+
{
77+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
78+
79+
for (int x = 0; x < pixelRow.Length; x++)
80+
{
81+
ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
82+
ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 2, 2));
83+
ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 4, 2));
84+
ushort a = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset + 6, 2));
85+
offset += 8;
86+
87+
pixelRow[x] = TPixel.FromRgba64(new Rgba64(r, g, b, a));
88+
}
89+
}
90+
}
91+
}
92+
else
93+
{
94+
for (int y = top; y < top + height; y++)
95+
{
96+
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
7597
int byteCount = pixelRow.Length * 8;
98+
7699
PixelOperations<TPixel>.Instance.FromRgba64Bytes(
77100
this.configuration,
78101
data.Slice(offset, byteCount),

src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ public static TPixel ColorFromRgba64Premultiplied<TPixel>(ushort r, ushort g, us
4545
return TPixel.FromRgba64(default);
4646
}
4747

48-
return TPixel.FromRgba64(new Rgba64((ushort)(r / a), (ushort)(g / a), (ushort)(b / a), a));
48+
float scale = 65535f / a;
49+
ushort ur = (ushort)Math.Min(r * scale, 65535);
50+
ushort ug = (ushort)Math.Min(g * scale, 65535);
51+
ushort ub = (ushort)Math.Min(b * scale, 65535);
52+
53+
return TPixel.FromRgba64(new Rgba64(ur, ug, ub, a));
4954
}
5055

5156
[MethodImpl(MethodImplOptions.AggressiveInlining)]

tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,19 @@ public void TiffDecoder_CanDecode_YccK<TPixel>(TestImageProvider<TPixel> provide
365365
image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.0001F), provider);
366366
}
367367

368+
[Theory]
369+
[WithFile(Issues3031, PixelTypes.Rgba64)]
370+
[WithFile(Rgba16BitAssociatedAlphaBigEndian, PixelTypes.Rgba64)]
371+
[WithFile(Rgba16BitAssociatedAlphaLittleEndian, PixelTypes.Rgba64)]
372+
public void TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha<TPixel>(TestImageProvider<TPixel> provider)
373+
where TPixel : unmanaged, IPixel<TPixel>
374+
{
375+
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
376+
image.DebugSave(provider);
377+
378+
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
379+
}
380+
368381
[Theory]
369382
[WithFile(Issues2454_A, PixelTypes.Rgba32)]
370383
[WithFile(Issues2454_B, PixelTypes.Rgba32)]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.Formats.Tiff.Utils;
5+
using SixLabors.ImageSharp.PixelFormats;
6+
7+
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils;
8+
9+
[Trait("Format", "Tiff")]
10+
public class TiffUtilitiesTest
11+
{
12+
[Theory]
13+
[InlineData(0, 0, 0, 0)]
14+
[InlineData(42, 84, 128, 0)]
15+
[InlineData(65535, 65535, 65535, 0)]
16+
public void ColorFromRgba64Premultiplied_WithZeroAlpha_ReturnsDefaultPixel(ushort r, ushort g, ushort b, ushort a)
17+
{
18+
Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied<Rgba64>(r, g, b, a);
19+
20+
Assert.Equal(default, actual);
21+
}
22+
23+
[Theory]
24+
[InlineData(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535)]
25+
[InlineData(32767, 0, 0, 65535, 32767, 0, 0, 65535)]
26+
[InlineData(0, 32767, 0, 65535, 0, 32767, 0, 65535)]
27+
[InlineData(0, 0, 32767, 65535, 0, 0, 32767, 65535)]
28+
public void ColorFromRgba64Premultiplied_WithNoAlpha_ReturnExpectedValues(ushort r, ushort g, ushort b, ushort a, ushort expectedR, ushort expectedG, ushort expectedB, ushort expectedA)
29+
{
30+
Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied<Rgba64>(r, g, b, a);
31+
32+
Assert.Equal(new Rgba64(expectedR, expectedG, expectedB, expectedA), actual);
33+
}
34+
35+
[Theory]
36+
[InlineData(32766, 0, 0, 32766, 65535, 0, 0, 32766)] // Red, 50% Alpha
37+
[InlineData(0, 32766, 0, 32766, 0, 65535, 0, 32766)] // Green, 50% Alpha
38+
[InlineData(0, 0, 32766, 32766, 0, 0, 65535, 32766)] // Blue, 50% Alpha
39+
[InlineData(8191, 0, 0, 16383, 32765, 0, 0, 16383)] // Red, 25% Alpha
40+
[InlineData(0, 8191, 0, 16383, 0, 32765, 0, 16383)] // Green, 25% Alpha
41+
[InlineData(0, 0, 8191, 16383, 0, 0, 32765, 16383)] // Blue, 25% Alpha
42+
[InlineData(8191, 0, 0, 0, 0, 0, 0, 0)] // Red, 0% Alpha
43+
[InlineData(0, 8191, 0, 0, 0, 0, 0, 0)] // Green, 0% Alpha
44+
[InlineData(0, 0, 8191, 0, 0, 0, 0, 0)] // Blue, 0% Alpha
45+
public void ColorFromRgba64Premultiplied_WithAlpha_ReturnExpectedValues(ushort r, ushort g, ushort b, ushort a, ushort expectedR, ushort expectedG, ushort expectedB, ushort expectedA)
46+
{
47+
Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied<Rgba64>(r, g, b, a);
48+
49+
Assert.Equal(new Rgba64(expectedR, expectedG, expectedB, expectedA), actual);
50+
}
51+
}

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,7 @@ public static class Tiff
11421142
public const string Issues2435 = "Tiff/Issues/Issue2435.tiff";
11431143
public const string Issues2454_A = "Tiff/Issues/Issue2454_A.tif";
11441144
public const string Issues2454_B = "Tiff/Issues/Issue2454_B.tif";
1145+
public const string Issues3031 = "Tiff/Issues/Issue3031.tiff";
11451146
public const string Issues2587 = "Tiff/Issues/Issue2587.tiff";
11461147
public const string Issues2679 = "Tiff/Issues/Issue2679.tiff";
11471148
public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff";
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)