Skip to content

Commit 9dddb8c

Browse files
committed
Port YCbCr ColorConverter to ARM
1 parent 809ba98 commit 9dddb8c

File tree

3 files changed

+144
-0
lines changed

3 files changed

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

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ private static JpegColorConverterBase GetYCbCrConverter(int precision)
138138
return new YCbCrAvx(precision);
139139
}
140140

141+
if (JpegColorConverterArm.IsSupported)
142+
{
143+
return new YCbCrArm(precision);
144+
}
145+
141146
if (JpegColorConverterVector.IsSupported)
142147
{
143148
return new YCbCrVector(precision);

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,23 @@ public void FromRgbToYCbCrAvx2(int seed) =>
256256
new JpegColorConverterBase.YCbCrScalar(8),
257257
precísion: 2);
258258

259+
[Theory]
260+
[MemberData(nameof(Seeds))]
261+
public void FromYCbCrArm(int seed) =>
262+
this.TestConversionToRgb(new JpegColorConverterBase.YCbCrArm(8),
263+
3,
264+
seed,
265+
new JpegColorConverterBase.YCbCrScalar(8));
266+
267+
[Theory]
268+
[MemberData(nameof(Seeds))]
269+
public void FromRgbToYCbCrArm(int seed) =>
270+
this.TestConversionFromRgb(new JpegColorConverterBase.YCbCrArm(8),
271+
3,
272+
seed,
273+
new JpegColorConverterBase.YCbCrScalar(8),
274+
precísion: 2);
275+
259276
[Theory]
260277
[MemberData(nameof(Seeds))]
261278
public void FromCmykAvx2(int seed) =>

0 commit comments

Comments
 (0)