Skip to content

Commit 5283d77

Browse files
Merge pull request #2397 from stefannikolei/stefannikolei/jpegcolorconverter_arm
Add Arm intrinsics to JpegColorConverter RGB
2 parents 0c8efbe + 70ff40b commit 5283d77

File tree

6 files changed

+148
-5
lines changed

6 files changed

+148
-5
lines changed

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ jobs:
5656
sdk: 6.0.x
5757
runtime: -x64
5858
codecov: false
59-
- os: buildjet-4vcpu-ubuntu-2204-arm
60-
framework: net6.0
61-
sdk: 6.0.x
62-
runtime: -x64
63-
codecov: false
6459
exclude:
6560
- isARM: false
6661
options:
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
10+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
11+
12+
internal abstract partial class JpegColorConverterBase
13+
{
14+
internal sealed class RgbArm : JpegColorConverterArm
15+
{
16+
public RgbArm(int precision)
17+
: base(JpegColorSpace.RGB, precision)
18+
{
19+
}
20+
21+
/// <inheritdoc/>
22+
public override void ConvertToRgbInplace(in ComponentValues values)
23+
{
24+
ref Vector128<float> rBase =
25+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
26+
ref Vector128<float> gBase =
27+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
28+
ref Vector128<float> bBase =
29+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
30+
31+
// Used for the color conversion
32+
var scale = Vector128.Create(1 / this.MaximumValue);
33+
nint n = (nint)(uint)values.Component0.Length / Vector128<float>.Count;
34+
for (nint i = 0; i < n; i++)
35+
{
36+
ref Vector128<float> r = ref Unsafe.Add(ref rBase, i);
37+
ref Vector128<float> g = ref Unsafe.Add(ref gBase, i);
38+
ref Vector128<float> b = ref Unsafe.Add(ref bBase, i);
39+
r = AdvSimd.Multiply(r, scale);
40+
g = AdvSimd.Multiply(g, scale);
41+
b = AdvSimd.Multiply(b, scale);
42+
}
43+
}
44+
45+
/// <inheritdoc/>
46+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
47+
{
48+
rLane.CopyTo(values.Component0);
49+
gLane.CopyTo(values.Component1);
50+
bLane.CopyTo(values.Component2);
51+
}
52+
}
53+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
using System.Runtime.Intrinsics;
4+
using System.Runtime.Intrinsics.Arm;
5+
using System.Runtime.Intrinsics.X86;
6+
7+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
8+
9+
internal abstract partial class JpegColorConverterBase
10+
{
11+
/// <summary>
12+
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
13+
/// based on <see cref="Avx"/> instructions.
14+
/// </summary>
15+
/// <remarks>
16+
/// Converters of this family would expect input buffers lengths to be
17+
/// divisible by 8 without a remainder.
18+
/// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks.
19+
/// DO NOT pass test data of invalid size to these converters as they
20+
/// potentially won't do a bound check and return a false positive result.
21+
/// </remarks>
22+
internal abstract class JpegColorConverterArm : JpegColorConverterBase
23+
{
24+
protected JpegColorConverterArm(JpegColorSpace colorSpace, int precision)
25+
: base(colorSpace, precision)
26+
{
27+
}
28+
29+
public static bool IsSupported => AdvSimd.IsSupported;
30+
31+
public sealed override bool IsAvailable => IsSupported;
32+
33+
public sealed override int ElementsPerBatch => Vector128<float>.Count;
34+
}
35+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ private static JpegColorConverterBase GetRgbConverter(int precision)
214214
return new RgbAvx(precision);
215215
}
216216

217+
if (JpegColorConverterArm.IsSupported)
218+
{
219+
return new RgbArm(precision);
220+
}
221+
217222
if (JpegColorConverterVector.IsSupported)
218223
{
219224
return new RgbScalar(precision);

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

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

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

tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System.Numerics;
55
using System.Runtime.Intrinsics;
6+
using System.Runtime.Intrinsics.X86;
7+
using Castle.Components.DictionaryAdapter;
68
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
79
using SixLabors.ImageSharp.Tests.TestUtilities;
810

@@ -30,6 +32,11 @@ public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amou
3032
[MemberData(nameof(NormalBlendFunctionData))]
3133
public void NormalBlendFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
3234
{
35+
if (!Avx.IsSupported)
36+
{
37+
return;
38+
}
39+
3340
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
3441
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
3542

@@ -57,6 +64,11 @@ public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount,
5764
[MemberData(nameof(MultiplyFunctionData))]
5865
public void MultiplyFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
5966
{
67+
if (!Avx.IsSupported)
68+
{
69+
return;
70+
}
71+
6072
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
6173
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
6274

@@ -84,6 +96,11 @@ public void AddFunction(TestVector4 back, TestVector4 source, float amount, Test
8496
[MemberData(nameof(AddFunctionData))]
8597
public void AddFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
8698
{
99+
if (!Avx.IsSupported)
100+
{
101+
return;
102+
}
103+
87104
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
88105
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
89106

@@ -111,6 +128,11 @@ public void SubtractFunction(TestVector4 back, TestVector4 source, float amount,
111128
[MemberData(nameof(SubtractFunctionData))]
112129
public void SubtractFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
113130
{
131+
if (!Avx.IsSupported)
132+
{
133+
return;
134+
}
135+
114136
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
115137
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
116138

@@ -138,6 +160,11 @@ public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, T
138160
[MemberData(nameof(ScreenFunctionData))]
139161
public void ScreenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
140162
{
163+
if (!Avx.IsSupported)
164+
{
165+
return;
166+
}
167+
141168
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
142169
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
143170

@@ -165,6 +192,11 @@ public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, T
165192
[MemberData(nameof(DarkenFunctionData))]
166193
public void DarkenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
167194
{
195+
if (!Avx.IsSupported)
196+
{
197+
return;
198+
}
199+
168200
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
169201
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
170202

@@ -192,6 +224,11 @@ public void LightenFunction(TestVector4 back, TestVector4 source, float amount,
192224
[MemberData(nameof(LightenFunctionData))]
193225
public void LightenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
194226
{
227+
if (!Avx.IsSupported)
228+
{
229+
return;
230+
}
231+
195232
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
196233
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
197234

@@ -219,6 +256,11 @@ public void OverlayFunction(TestVector4 back, TestVector4 source, float amount,
219256
[MemberData(nameof(OverlayFunctionData))]
220257
public void OverlayFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
221258
{
259+
if (!Avx.IsSupported)
260+
{
261+
return;
262+
}
263+
222264
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
223265
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
224266

@@ -246,6 +288,11 @@ public void HardLightFunction(TestVector4 back, TestVector4 source, float amount
246288
[MemberData(nameof(HardLightFunctionData))]
247289
public void HardLightFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected)
248290
{
291+
if (!Avx.IsSupported)
292+
{
293+
return;
294+
}
295+
249296
Vector256<float> back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W);
250297
Vector256<float> source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
251298

0 commit comments

Comments
 (0)