Skip to content

Commit 9badd81

Browse files
Merge pull request #2937 from SixLabors/bp/tiff-jpeg-cmyk
Add Full Support for JPEG Tiff PhotometricInterpretation.Separated
2 parents 166a846 + 1aeb1af commit 9badd81

38 files changed

+1246
-45
lines changed

src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ internal static void ConvertToRgbInPlace(ComponentValues values, float maxValue)
7575

7676
internal static void ConvertFromRgb(ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
7777
{
78+
// TODO: This doesn't seem correct. We should be scaling to the maximum value here.
7879
rLane.CopyTo(values.Component0);
7980
gLane.CopyTo(values.Component1);
8081
bLane.CopyTo(values.Component2);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers;
5+
using System.Numerics;
6+
using System.Runtime.InteropServices;
7+
using SixLabors.ImageSharp.ColorProfiles;
8+
using SixLabors.ImageSharp.ColorProfiles.Icc;
9+
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
10+
11+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
12+
13+
internal abstract partial class JpegColorConverterBase
14+
{
15+
/// <summary>
16+
/// Color converter for tiff images, which use the jpeg compression and CMYK colorspace.
17+
/// </summary>
18+
internal sealed class TiffCmykScalar : JpegColorConverterScalar
19+
{
20+
public TiffCmykScalar(int precision)
21+
: base(JpegColorSpace.TiffCmyk, precision)
22+
{
23+
}
24+
25+
/// <inheritdoc/>
26+
public override void ConvertToRgbInPlace(in ComponentValues values)
27+
=> ConvertToRgbInPlace(in values, this.MaximumValue);
28+
29+
/// <inheritdoc/>
30+
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile)
31+
=> ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue);
32+
33+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
34+
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
35+
36+
public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue)
37+
{
38+
Span<float> c0 = values.Component0;
39+
Span<float> c1 = values.Component1;
40+
Span<float> c2 = values.Component2;
41+
Span<float> c3 = values.Component3;
42+
43+
float scale = 1 / maxValue;
44+
for (int i = 0; i < c0.Length; i++)
45+
{
46+
float c = c0[i] * scale;
47+
float m = c1[i] * scale;
48+
float y = c2[i] * scale;
49+
float k = 1 - (c3[i] * scale);
50+
51+
c0[i] = (1 - c) * k;
52+
c1[i] = (1 - m) * k;
53+
c2[i] = (1 - y) * k;
54+
}
55+
}
56+
57+
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
58+
{
59+
Span<float> c = values.Component0;
60+
Span<float> m = values.Component1;
61+
Span<float> y = values.Component2;
62+
Span<float> k = values.Component3;
63+
64+
for (int i = 0; i < c.Length; i++)
65+
{
66+
float ctmp = 255F - rLane[i];
67+
float mtmp = 255F - gLane[i];
68+
float ytmp = 255F - bLane[i];
69+
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp);
70+
71+
if (ktmp >= 255F)
72+
{
73+
ctmp = 0F;
74+
mtmp = 0F;
75+
ytmp = 0F;
76+
}
77+
else
78+
{
79+
float divisor = 1 / (255F - ktmp);
80+
ctmp = (ctmp - ktmp) * divisor;
81+
mtmp = (mtmp - ktmp) * divisor;
82+
ytmp = (ytmp - ktmp) * divisor;
83+
}
84+
85+
c[i] = ctmp * maxValue;
86+
m[i] = mtmp * maxValue;
87+
y[i] = ytmp * maxValue;
88+
k[i] = ktmp;
89+
}
90+
}
91+
92+
public static void ConvertToRgbInPlaceWithIcc(Configuration configuration, IccProfile profile, in ComponentValues values, float maxValue)
93+
{
94+
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 4);
95+
Span<float> packed = memoryOwner.Memory.Span;
96+
97+
Span<float> c0 = values.Component0;
98+
Span<float> c1 = values.Component1;
99+
Span<float> c2 = values.Component2;
100+
Span<float> c3 = values.Component3;
101+
102+
PackedNormalizeInterleave4(c0, c1, c2, c3, packed, maxValue);
103+
104+
Span<Cmyk> source = MemoryMarshal.Cast<float, Cmyk>(packed);
105+
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(packed)[..source.Length];
106+
107+
ColorConversionOptions options = new()
108+
{
109+
SourceIccProfile = profile,
110+
TargetIccProfile = CompactSrgbV4Profile.Profile,
111+
};
112+
ColorProfileConverter converter = new(options);
113+
converter.Convert<Cmyk, Rgb>(source, destination);
114+
115+
UnpackDeinterleave3(MemoryMarshal.Cast<float, Vector3>(packed)[..source.Length], c0, c1, c2);
116+
}
117+
}
118+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.Intrinsics;
7+
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
8+
9+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
10+
11+
internal abstract partial class JpegColorConverterBase
12+
{
13+
internal sealed class TiffCmykVector128 : JpegColorConverterVector128
14+
{
15+
public TiffCmykVector128(int precision)
16+
: base(JpegColorSpace.TiffCmyk, precision)
17+
{
18+
}
19+
20+
/// <inheritdoc/>
21+
public override void ConvertToRgbInPlace(in ComponentValues values)
22+
{
23+
ref Vector128<float> c0Base =
24+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
25+
ref Vector128<float> c1Base =
26+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
27+
ref Vector128<float> c2Base =
28+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
29+
ref Vector128<float> c3Base =
30+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
31+
32+
Vector128<float> scale = Vector128.Create(1 / this.MaximumValue);
33+
34+
nuint n = values.Component0.Vector128Count<float>();
35+
for (nuint i = 0; i < n; i++)
36+
{
37+
ref Vector128<float> c = ref Unsafe.Add(ref c0Base, i);
38+
ref Vector128<float> m = ref Unsafe.Add(ref c1Base, i);
39+
ref Vector128<float> y = ref Unsafe.Add(ref c2Base, i);
40+
Vector128<float> k = Unsafe.Add(ref c3Base, i);
41+
42+
k = Vector128<float>.One - (k * scale);
43+
c = (Vector128<float>.One - (c * scale)) * k;
44+
m = (Vector128<float>.One - (m * scale)) * k;
45+
y = (Vector128<float>.One - (y * scale)) * k;
46+
}
47+
}
48+
49+
/// <inheritdoc/>
50+
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile)
51+
=> TiffCmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue);
52+
53+
/// <inheritdoc/>
54+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
55+
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
56+
57+
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
58+
{
59+
ref Vector128<float> destC =
60+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
61+
ref Vector128<float> destM =
62+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
63+
ref Vector128<float> destY =
64+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
65+
ref Vector128<float> destK =
66+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
67+
68+
ref Vector128<float> srcR =
69+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
70+
ref Vector128<float> srcG =
71+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
72+
ref Vector128<float> srcB =
73+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));
74+
75+
Vector128<float> scale = Vector128.Create(maxValue);
76+
77+
nuint n = values.Component0.Vector128Count<float>();
78+
for (nuint i = 0; i < n; i++)
79+
{
80+
Vector128<float> ctmp = scale - Unsafe.Add(ref srcR, i);
81+
Vector128<float> mtmp = scale - Unsafe.Add(ref srcG, i);
82+
Vector128<float> ytmp = scale - Unsafe.Add(ref srcB, i);
83+
Vector128<float> ktmp = Vector128.Min(ctmp, Vector128.Min(mtmp, ytmp));
84+
85+
Vector128<float> kMask = ~Vector128.Equals(ktmp, scale);
86+
Vector128<float> divisor = Vector128<float>.One / (scale - ktmp);
87+
88+
ctmp = ((ctmp - ktmp) * divisor) & kMask;
89+
mtmp = ((mtmp - ktmp) * divisor) & kMask;
90+
ytmp = ((ytmp - ktmp) * divisor) & kMask;
91+
92+
Unsafe.Add(ref destC, i) = ctmp * scale;
93+
Unsafe.Add(ref destM, i) = mtmp * scale;
94+
Unsafe.Add(ref destY, i) = ytmp * scale;
95+
Unsafe.Add(ref destK, i) = ktmp;
96+
}
97+
}
98+
}
99+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.Intrinsics;
7+
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
8+
9+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
10+
11+
internal abstract partial class JpegColorConverterBase
12+
{
13+
internal sealed class TiffCmykVector256 : JpegColorConverterVector256
14+
{
15+
public TiffCmykVector256(int precision)
16+
: base(JpegColorSpace.TiffCmyk, precision)
17+
{
18+
}
19+
20+
/// <inheritdoc/>
21+
public override void ConvertToRgbInPlace(in ComponentValues values)
22+
{
23+
ref Vector256<float> c0Base =
24+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
25+
ref Vector256<float> c1Base =
26+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
27+
ref Vector256<float> c2Base =
28+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
29+
ref Vector256<float> c3Base =
30+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
31+
32+
Vector256<float> scale = Vector256.Create(1 / this.MaximumValue);
33+
34+
nuint n = values.Component0.Vector256Count<float>();
35+
for (nuint i = 0; i < n; i++)
36+
{
37+
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i);
38+
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i);
39+
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i);
40+
Vector256<float> k = Unsafe.Add(ref c3Base, i);
41+
42+
k = Vector256<float>.One - (k * scale);
43+
c = (Vector256<float>.One - (c * scale)) * k;
44+
m = (Vector256<float>.One - (m * scale)) * k;
45+
y = (Vector256<float>.One - (y * scale)) * k;
46+
}
47+
}
48+
49+
/// <inheritdoc/>
50+
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile)
51+
=> CmykScalar.ConvertToRgbInPlaceWithIcc(configuration, profile, values, this.MaximumValue);
52+
53+
/// <inheritdoc/>
54+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
55+
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
56+
57+
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
58+
{
59+
ref Vector256<float> destC =
60+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
61+
ref Vector256<float> destM =
62+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
63+
ref Vector256<float> destY =
64+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
65+
ref Vector256<float> destK =
66+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
67+
68+
ref Vector256<float> srcR =
69+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane));
70+
ref Vector256<float> srcG =
71+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane));
72+
ref Vector256<float> srcB =
73+
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane));
74+
75+
Vector256<float> scale = Vector256.Create(maxValue);
76+
77+
nuint n = values.Component0.Vector256Count<float>();
78+
for (nuint i = 0; i < n; i++)
79+
{
80+
Vector256<float> ctmp = scale - Unsafe.Add(ref srcR, i);
81+
Vector256<float> mtmp = scale - Unsafe.Add(ref srcG, i);
82+
Vector256<float> ytmp = scale - Unsafe.Add(ref srcB, i);
83+
Vector256<float> ktmp = Vector256.Min(ctmp, Vector256.Min(mtmp, ytmp));
84+
85+
Vector256<float> kMask = ~Vector256.Equals(ktmp, scale);
86+
Vector256<float> divisor = Vector256<float>.One / (scale - ktmp);
87+
88+
ctmp = ((ctmp - ktmp) * divisor) & kMask;
89+
mtmp = ((mtmp - ktmp) * divisor) & kMask;
90+
ytmp = ((ytmp - ktmp) * divisor) & kMask;
91+
92+
Unsafe.Add(ref destC, i) = ctmp * scale;
93+
Unsafe.Add(ref destM, i) = mtmp * scale;
94+
Unsafe.Add(ref destY, i) = ytmp * scale;
95+
Unsafe.Add(ref destK, i) = ktmp;
96+
}
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)