Skip to content

Commit 6784acf

Browse files
committed
2 parents 9a07ee9 + d7cd46f commit 6784acf

File tree

6 files changed

+384
-43
lines changed

6 files changed

+384
-43
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!)
6464

6565
- Using [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
6666
- Make sure you have the latest version installed
67-
- Make sure you have [the .NET 6 SDK](https://www.microsoft.com/net/core#windows) installed
67+
- Make sure you have [the .NET 7 SDK](https://www.microsoft.com/net/core#windows) installed
6868

6969
Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:
7070

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
9+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
10+
11+
internal abstract partial class JpegColorConverterBase
12+
{
13+
internal sealed class CmykArm64 : JpegColorConverterArm64
14+
{
15+
public CmykArm64(int precision)
16+
: base(JpegColorSpace.Cmyk, precision)
17+
{
18+
}
19+
20+
/// <inheritdoc/>
21+
public override void ConvertToRgbInplace(in ComponentValues values)
22+
{
23+
ref Vector128<float> c0Base =
24+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
25+
ref Vector128<float> c1Base =
26+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
27+
ref Vector128<float> c2Base =
28+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
29+
ref Vector128<float> c3Base =
30+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
31+
32+
// Used for the color conversion
33+
var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue));
34+
35+
nint n = (nint)(uint)values.Component0.Length / Vector128<float>.Count;
36+
for (nint i = 0; i < n; i++)
37+
{
38+
ref Vector128<float> c = ref Unsafe.Add(ref c0Base, i);
39+
ref Vector128<float> m = ref Unsafe.Add(ref c1Base, i);
40+
ref Vector128<float> y = ref Unsafe.Add(ref c2Base, i);
41+
Vector128<float> k = Unsafe.Add(ref c3Base, i);
42+
43+
k = AdvSimd.Multiply(k, scale);
44+
c = AdvSimd.Multiply(c, k);
45+
m = AdvSimd.Multiply(m, k);
46+
y = AdvSimd.Multiply(y, k);
47+
}
48+
}
49+
50+
/// <inheritdoc/>
51+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
52+
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
53+
54+
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
55+
{
56+
ref Vector128<float> destC =
57+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
58+
ref Vector128<float> destM =
59+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
60+
ref Vector128<float> destY =
61+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
62+
ref Vector128<float> destK =
63+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component3));
64+
65+
ref Vector128<float> srcR =
66+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
67+
ref Vector128<float> srcG =
68+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
69+
ref Vector128<float> srcB =
70+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));
71+
72+
var scale = Vector128.Create(maxValue);
73+
74+
nint n = (nint)(uint)values.Component0.Length / Vector128<float>.Count;
75+
for (nint i = 0; i < n; i++)
76+
{
77+
Vector128<float> ctmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcR, i));
78+
Vector128<float> mtmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcG, i));
79+
Vector128<float> ytmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcB, i));
80+
Vector128<float> ktmp = AdvSimd.Min(ctmp, AdvSimd.Min(mtmp, ytmp));
81+
82+
Vector128<float> kMask = AdvSimd.Not(AdvSimd.CompareEqual(ktmp, scale));
83+
84+
ctmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ctmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask);
85+
mtmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(mtmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask);
86+
ytmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ytmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask);
87+
88+
Unsafe.Add(ref destC, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ctmp, scale));
89+
Unsafe.Add(ref destM, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(mtmp, scale));
90+
Unsafe.Add(ref destY, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ytmp, scale));
91+
Unsafe.Add(ref destK, i) = AdvSimd.Subtract(scale, ktmp);
92+
}
93+
}
94+
}
95+
}
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 JpegColorConverterArm64 : JpegColorConverterBase
23+
{
24+
protected JpegColorConverterArm64(JpegColorSpace colorSpace, int precision)
25+
: base(colorSpace, precision)
26+
{
27+
}
28+
29+
public static bool IsSupported => AdvSimd.Arm64.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
@@ -176,6 +176,11 @@ private static JpegColorConverterBase GetCmykConverter(int precision)
176176
return new CmykAvx(precision);
177177
}
178178

179+
if (JpegColorConverterArm64.IsSupported)
180+
{
181+
return new CmykArm64(precision);
182+
}
183+
179184
if (JpegColorConverterVector.IsSupported)
180185
{
181186
return new CmykVector(precision);

tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.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.CmykAvx(8).ConvertToRgbInplace(values);
3939
}
40+
41+
[Benchmark]
42+
public void SimdVectorArm64()
43+
{
44+
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
45+
46+
new JpegColorConverterBase.CmykArm64(8).ConvertToRgbInplace(values);
47+
}
4048
}

0 commit comments

Comments
 (0)