Skip to content

Commit 60c39bb

Browse files
committed
Port YCCK to arm
1 parent d6a3d7c commit 60c39bb

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 System.Runtime.Intrinsics.Arm;
8+
using System.Runtime.Intrinsics.X86;
9+
using static SixLabors.ImageSharp.SimdUtils;
10+
11+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
12+
13+
internal abstract partial class JpegColorConverterBase
14+
{
15+
internal sealed class YccKArm64 : JpegColorConverterArm64
16+
{
17+
public YccKArm64(int precision)
18+
: base(JpegColorSpace.Ycck, precision)
19+
{
20+
}
21+
22+
/// <inheritdoc/>
23+
public override void ConvertToRgbInplace(in ComponentValues values)
24+
{
25+
ref Vector128<float> c0Base =
26+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
27+
ref Vector128<float> c1Base =
28+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
29+
ref Vector128<float> c2Base =
30+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
31+
ref Vector128<float> kBase =
32+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
33+
34+
// Used for the color conversion
35+
var chromaOffset = Vector128.Create(-this.HalfValue);
36+
var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue));
37+
var max = Vector128.Create(this.MaximumValue);
38+
var rCrMult = Vector128.Create(YCbCrScalar.RCrMult);
39+
var gCbMult = Vector128.Create(-YCbCrScalar.GCbMult);
40+
var gCrMult = Vector128.Create(-YCbCrScalar.GCrMult);
41+
var bCbMult = Vector128.Create(YCbCrScalar.BCbMult);
42+
43+
// Walking 8 elements at one step:
44+
nuint n = (uint)values.Component0.Length / (uint)Vector128<float>.Count;
45+
for (nuint i = 0; i < n; i++)
46+
{
47+
// y = yVals[i];
48+
// cb = cbVals[i] - 128F;
49+
// cr = crVals[i] - 128F;
50+
// k = kVals[i] / 256F;
51+
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
52+
ref Vector128<float> c1 = ref Unsafe.Add(ref c1Base, i);
53+
ref Vector128<float> c2 = ref Unsafe.Add(ref c2Base, i);
54+
Vector128<float> y = c0;
55+
Vector128<float> cb = AdvSimd.Add(c1, chromaOffset);
56+
Vector128<float> cr = AdvSimd.Add(c2, chromaOffset);
57+
Vector128<float> scaledK = AdvSimd.Multiply(Unsafe.Add(ref kBase, i), scale);
58+
59+
// r = y + (1.402F * cr);
60+
// g = y - (0.344136F * cb) - (0.714136F * cr);
61+
// b = y + (1.772F * cb);
62+
Vector128<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
63+
Vector128<float> g =
64+
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
65+
Vector128<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult);
66+
67+
r = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(r));
68+
g = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(g));
69+
b = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(b));
70+
71+
r = AdvSimd.Multiply(r, scaledK);
72+
g = AdvSimd.Multiply(g, scaledK);
73+
b = AdvSimd.Multiply(b, scaledK);
74+
75+
c0 = r;
76+
c1 = g;
77+
c2 = b;
78+
}
79+
}
80+
81+
/// <inheritdoc/>
82+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
83+
{
84+
// rgb -> cmyk
85+
CmykArm64.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
86+
87+
// cmyk -> ycck
88+
ref Vector128<float> destY =
89+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
90+
ref Vector128<float> destCb =
91+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
92+
ref Vector128<float> destCr =
93+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
94+
95+
ref Vector128<float> srcR = ref destY;
96+
ref Vector128<float> srcG = ref destCb;
97+
ref Vector128<float> srcB = ref destCr;
98+
99+
// Used for the color conversion
100+
var maxSampleValue = Vector128.Create(this.MaximumValue);
101+
102+
var chromaOffset = Vector128.Create(this.HalfValue);
103+
104+
var f0299 = Vector128.Create(0.299f);
105+
var f0587 = Vector128.Create(0.587f);
106+
var f0114 = Vector128.Create(0.114f);
107+
var fn0168736 = Vector128.Create(-0.168736f);
108+
var fn0331264 = Vector128.Create(-0.331264f);
109+
var fn0418688 = Vector128.Create(-0.418688f);
110+
var fn0081312F = Vector128.Create(-0.081312F);
111+
var f05 = Vector128.Create(0.5f);
112+
113+
nuint n = (uint)values.Component0.Length / (uint)Vector128<float>.Count;
114+
for (nuint i = 0; i < n; i++)
115+
{
116+
Vector128<float> r = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i));
117+
Vector128<float> g = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i));
118+
Vector128<float> b = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcB, i));
119+
120+
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
121+
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
122+
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
123+
Vector128<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r);
124+
Vector128<float> cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r));
125+
Vector128<float> cr = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(fn0081312F, b), fn0418688, g), f05, r));
126+
127+
Unsafe.Add(ref destY, i) = y;
128+
Unsafe.Add(ref destCb, i) = cb;
129+
Unsafe.Add(ref destCr, i) = cr;
130+
}
131+
}
132+
}
133+
}

src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ private static JpegColorConverterBase GetYccKConverter(int precision)
167167
return new YccKVector(precision);
168168
}
169169

170+
if (JpegColorConverterArm64.IsSupported)
171+
{
172+
return new YccKArm64(precision);
173+
}
174+
170175
return new YccKScalar(precision);
171176
}
172177

tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,12 @@ public void SimdVectorAvx2()
3737

3838
new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values);
3939
}
40+
41+
[Benchmark]
42+
public void SimdVectorArm64()
43+
{
44+
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
45+
46+
new JpegColorConverterBase.YccKArm64(8).ConvertToRgbInplace(values);
47+
}
4048
}

tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,23 @@ public void FromRgbToYccKAvx2(int seed) =>
374374
new JpegColorConverterBase.YccKScalar(8),
375375
precísion: 4);
376376

377+
[Theory]
378+
[MemberData(nameof(Seeds))]
379+
public void FromYccKArm64(int seed) =>
380+
this.TestConversionToRgb( new JpegColorConverterBase.YccKArm64(8),
381+
4,
382+
seed,
383+
new JpegColorConverterBase.YccKScalar(8));
384+
385+
[Theory]
386+
[MemberData(nameof(Seeds))]
387+
public void FromRgbToYccKArm64(int seed) =>
388+
this.TestConversionFromRgb(new JpegColorConverterBase.YccKArm64(8),
389+
4,
390+
seed,
391+
new JpegColorConverterBase.YccKScalar(8),
392+
precísion: 4);
393+
377394
private void TestConversionToRgb(
378395
JpegColorConverterBase converter,
379396
int componentCount,

0 commit comments

Comments
 (0)