Skip to content

Commit

Permalink
Merge pull request #2409 from stefannikolei/stefannikolei/arm/colorco…
Browse files Browse the repository at this point in the history
…nvertergrayscale

Port GrayscalConverter to Arm
  • Loading branch information
JimBobSquarePants authored Mar 27, 2023
2 parents 9756ae9 + 809ba98 commit 49c2262
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.PixelFormats;

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

/// <summary>
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
/// </summary>
/// <remarks>ret = (vm0 * vm1) + va</remarks>
/// <param name="va">The vector to add to the intermediate result.</param>
/// <param name="vm0">The first vector to multiply.</param>
/// <param name="vm1">The second vector to multiply.</param>
/// <returns>The <see cref="Vector256{T}"/>.</returns>
[MethodImpl(InliningOptions.AlwaysInline)]
public static Vector128<float> MultiplyAdd(
Vector128<float> va,
Vector128<float> vm0,
Vector128<float> vm1)
{
if (Fma.IsSupported)
{
return Fma.MultiplyAdd(vm1, vm0, va);
}

if (AdvSimd.IsSupported)
{
return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va);
}

return Sse.Add(Sse.Multiply(vm0, vm1), va);
}

/// <summary>
/// Performs a multiplication and a subtraction of the <see cref="Vector256{Single}"/>.
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using static SixLabors.ImageSharp.SimdUtils;

namespace SixLabors.ImageSharp.Formats.Jpeg.Components;

internal abstract partial class JpegColorConverterBase
{
internal sealed class GrayscaleArm : JpegColorConverterArm
{
public GrayscaleArm(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}

/// <inheritdoc/>
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector128<float> c0Base =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));

// Used for the color conversion
var scale = Vector128.Create(1 / this.MaximumValue);

nuint n = (uint)values.Component0.Length / (uint)Vector128<float>.Count;
for (nuint i = 0; i < n; i++)
{
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = AdvSimd.Multiply(c0, scale);
}
}

/// <inheritdoc/>
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
{
ref Vector128<float> destLuminance =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));

ref Vector128<float> srcRed =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
ref Vector128<float> srcGreen =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
ref Vector128<float> srcBlue =
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));

// Used for the color conversion
var f0299 = Vector128.Create(0.299f);
var f0587 = Vector128.Create(0.587f);
var f0114 = Vector128.Create(0.114f);

nuint n = (uint)values.Component0.Length / (uint)Vector128<float>.Count;
for (nuint i = 0; i < n; i++)
{
ref Vector128<float> r = ref Unsafe.Add(ref srcRed, i);
ref Vector128<float> g = ref Unsafe.Add(ref srcGreen, i);
ref Vector128<float> b = ref Unsafe.Add(ref srcBlue, i);

// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ private static JpegColorConverterBase GetGrayScaleConverter(int precision)
return new GrayscaleAvx(precision);
}

if (JpegColorConverterArm.IsSupported)
{
return new GrayscaleArm(precision);
}

if (JpegColorConverterVector.IsSupported)
{
return new GrayScaleVector(precision);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@ public void SimdVectorAvx()

new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values);
}

[Benchmark]
public void SimdVectorArm()
{
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);

new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values);
}
}
19 changes: 18 additions & 1 deletion tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,23 @@ public void FromRgbToGrayscaleAvx2(int seed) =>
new JpegColorConverterBase.GrayscaleScalar(8),
precísion: 3);

[Theory]
[MemberData(nameof(Seeds))]
public void FromGrayscaleArm(int seed) =>
this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8),
1,
seed,
new JpegColorConverterBase.GrayscaleScalar(8));

[Theory]
[MemberData(nameof(Seeds))]
public void FromRgbToGrayscaleArm(int seed) =>
this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8),
1,
seed,
new JpegColorConverterBase.GrayscaleScalar(8),
precísion: 3);

[Theory]
[MemberData(nameof(Seeds))]
public void FromRgbAvx2(int seed) =>
Expand Down Expand Up @@ -480,7 +497,7 @@ private static void ValidateConversionFromRgb(
JpegColorConverterBase baseLineConverter,
int precision = 4)
{
// arrange
// arrange
JpegColorConverterBase.ComponentValues actual = CreateRandomValues(TestBufferLength, componentCount, seed);
JpegColorConverterBase.ComponentValues expected = CreateRandomValues(TestBufferLength, componentCount, seed);
Random rnd = new(seed);
Expand Down

0 comments on commit 49c2262

Please sign in to comment.