From c2558dd60c31a2e2fea92f714bec4c9ef042b872 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 12 Mar 2023 16:22:53 +0100 Subject: [PATCH] Add Arm intrinsics to JpegColorConverter --- .../JpegColorConverter.RgbArm.cs | 53 +++++++++++++++++++ .../ColorConverters/JpegColorConverterArm.cs | 35 ++++++++++++ .../ColorConverters/JpegColorConverterBase.cs | 5 ++ .../PixelBlenders/PorterDuffFunctionsTests.cs | 37 +++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs new file mode 100644 index 0000000000..73a81361f3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs @@ -0,0 +1,53 @@ +// 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 System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class RgbArm : JpegColorConverterArm + { + public RgbArm(int precision) + : base(JpegColorSpace.RGB, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 rBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 gBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 bBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + var scale = Vector128.Create(1 / this.MaximumValue); + nint n = values.Component0.Length / Vector128.Count; + for (nint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref rBase, i); + ref Vector128 g = ref Unsafe.Add(ref gBase, i); + ref Vector128 b = ref Unsafe.Add(ref bBase, i); + r = AdvSimd.Multiply(r, scale); + g = AdvSimd.Multiply(g, scale); + b = AdvSimd.Multiply(b, scale); + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + rLane.CopyTo(values.Component0); + gLane.CopyTo(values.Component1); + bLane.CopyTo(values.Component2); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs new file mode 100644 index 0000000000..bfbbf200c8 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + /// + /// Converters of this family would expect input buffers lengths to be + /// divisible by 8 without a remainder. + /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. + /// DO NOT pass test data of invalid size to these converters as they + /// potentially won't do a bound check and return a false positive result. + /// + internal abstract class JpegColorConverterArm : JpegColorConverterBase + { + protected JpegColorConverterArm(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => AdvSimd.IsSupported; + + public sealed override bool IsAvailable => IsSupported; + + public sealed override int ElementsPerBatch => Vector128.Count; + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 62f48af16e..66f0e9f5a5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -214,6 +214,11 @@ internal abstract partial class JpegColorConverterBase return new RgbAvx(precision); } + if (JpegColorConverterArm.IsSupported) + { + return new RgbArm(precision); + } + if (JpegColorConverterVector.IsSupported) { return new RgbScalar(precision); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 189d21f1e6..f46c2e9702 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -3,6 +3,8 @@ using System.Numerics; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Castle.Components.DictionaryAdapter; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -84,6 +86,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(AddFunctionData))] public void AddFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -111,6 +118,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(SubtractFunctionData))] public void SubtractFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -138,6 +150,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(ScreenFunctionData))] public void ScreenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -165,6 +182,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(DarkenFunctionData))] public void DarkenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -192,6 +214,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(LightenFunctionData))] public void LightenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -219,6 +246,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(OverlayFunctionData))] public void OverlayFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); @@ -246,6 +278,11 @@ public class PorterDuffFunctionsTests [MemberData(nameof(HardLightFunctionData))] public void HardLightFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { + if (!Avx.IsSupported) + { + return; + } + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);