Skip to content

Commit a4556fd

Browse files
committed
Port GrayscalConverter to Arm
1 parent d7cd46f commit a4556fd

File tree

4 files changed

+124
-1
lines changed

4 files changed

+124
-1
lines changed

src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Runtime.CompilerServices;
55
using System.Runtime.InteropServices;
66
using System.Runtime.Intrinsics;
7+
using System.Runtime.Intrinsics.Arm;
78
using System.Runtime.Intrinsics.X86;
89
using SixLabors.ImageSharp.PixelFormats;
910

@@ -554,6 +555,34 @@ public static Vector256<float> MultiplyAdd(
554555
return Avx.Add(Avx.Multiply(vm0, vm1), va);
555556
}
556557

558+
/// <summary>
559+
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
560+
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
561+
/// </summary>
562+
/// <remarks>ret = (vm0 * vm1) + va</remarks>
563+
/// <param name="va">The vector to add to the intermediate result.</param>
564+
/// <param name="vm0">The first vector to multiply.</param>
565+
/// <param name="vm1">The second vector to multiply.</param>
566+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
567+
[MethodImpl(InliningOptions.AlwaysInline)]
568+
public static Vector128<float> MultiplyAdd(
569+
in Vector128<float> va,
570+
in Vector128<float> vm0,
571+
in Vector128<float> vm1)
572+
{
573+
if (Fma.IsSupported)
574+
{
575+
return Fma.MultiplyAdd(vm1, vm0, va);
576+
}
577+
578+
if (AdvSimd.IsSupported)
579+
{
580+
return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va);
581+
}
582+
583+
return Avx.Add(Avx.Multiply(vm0, vm1), va);
584+
}
585+
557586
/// <summary>
558587
/// Performs a multiplication and a subtraction of the <see cref="Vector256{Single}"/>.
559588
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 GrayscaleArm : JpegColorConverterArm
16+
{
17+
public GrayscaleArm(int precision)
18+
: base(JpegColorSpace.Grayscale, 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+
28+
// Used for the color conversion
29+
var scale = Vector128.Create(1 / this.MaximumValue);
30+
31+
nint n = values.Component0.Length / Vector128<float>.Count;
32+
for (nint i = 0; i < n; i++)
33+
{
34+
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
35+
c0 = AdvSimd.Multiply(c0, scale);
36+
}
37+
}
38+
39+
/// <inheritdoc/>
40+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
41+
{
42+
ref Vector128<float> destLuminance =
43+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
44+
45+
ref Vector128<float> srcRed =
46+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
47+
ref Vector128<float> srcGreen =
48+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
49+
ref Vector128<float> srcBlue =
50+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));
51+
52+
// Used for the color conversion
53+
var f0299 = Vector128.Create(0.299f);
54+
var f0587 = Vector128.Create(0.587f);
55+
var f0114 = Vector128.Create(0.114f);
56+
57+
nint n = values.Component0.Length / Vector256<float>.Count;
58+
for (nint i = 0; i < n; i++)
59+
{
60+
ref Vector128<float> r = ref Unsafe.Add(ref srcRed, i);
61+
ref Vector128<float> g = ref Unsafe.Add(ref srcGreen, i);
62+
ref Vector128<float> b = ref Unsafe.Add(ref srcBlue, i);
63+
64+
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
65+
Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r);
66+
}
67+
}
68+
}
69+
}

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

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

3030
new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values);
3131
}
32+
33+
[Benchmark]
34+
public void SimdVectorArm()
35+
{
36+
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
37+
38+
new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values);
39+
}
3240
}

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,23 @@ public void FromRgbToGrayscaleAvx2(int seed) =>
307307
new JpegColorConverterBase.GrayscaleScalar(8),
308308
precísion: 3);
309309

310+
[Theory]
311+
[MemberData(nameof(Seeds))]
312+
public void FromGrayscaleArm(int seed) =>
313+
this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8),
314+
1,
315+
seed,
316+
new JpegColorConverterBase.GrayscaleScalar(8));
317+
318+
[Theory]
319+
[MemberData(nameof(Seeds))]
320+
public void FromRgbToGrayscaleArm(int seed) =>
321+
this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8),
322+
1,
323+
seed,
324+
new JpegColorConverterBase.GrayscaleScalar(8),
325+
precísion: 3);
326+
310327
[Theory]
311328
[MemberData(nameof(Seeds))]
312329
public void FromRgbAvx2(int seed) =>
@@ -480,7 +497,7 @@ private static void ValidateConversionFromRgb(
480497
JpegColorConverterBase baseLineConverter,
481498
int precision = 4)
482499
{
483-
// arrange
500+
// arrange
484501
JpegColorConverterBase.ComponentValues actual = CreateRandomValues(TestBufferLength, componentCount, seed);
485502
JpegColorConverterBase.ComponentValues expected = CreateRandomValues(TestBufferLength, componentCount, seed);
486503
Random rnd = new(seed);

0 commit comments

Comments
 (0)