Skip to content

Commit 3fbbde9

Browse files
Merge pull request #2917 from SixLabors/js/simplify-jpeg-converters
Modernize JPEG Color Converters
2 parents 22a06d6 + 5b94795 commit 3fbbde9

File tree

44 files changed

+1527
-1371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1527
-1371
lines changed

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

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -616,35 +616,7 @@ public static Vector256<float> MultiplyAdd(
616616
return Fma.MultiplyAdd(vm1, vm0, va);
617617
}
618618

619-
return Avx.Add(Avx.Multiply(vm0, vm1), va);
620-
}
621-
622-
/// <summary>
623-
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
624-
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
625-
/// </summary>
626-
/// <remarks>ret = (vm0 * vm1) + va</remarks>
627-
/// <param name="va">The vector to add to the intermediate result.</param>
628-
/// <param name="vm0">The first vector to multiply.</param>
629-
/// <param name="vm1">The second vector to multiply.</param>
630-
/// <returns>The <see cref="Vector256{T}"/>.</returns>
631-
[MethodImpl(InliningOptions.AlwaysInline)]
632-
public static Vector128<float> MultiplyAdd(
633-
Vector128<float> va,
634-
Vector128<float> vm0,
635-
Vector128<float> vm1)
636-
{
637-
if (Fma.IsSupported)
638-
{
639-
return Fma.MultiplyAdd(vm1, vm0, va);
640-
}
641-
642-
if (AdvSimd.IsSupported)
643-
{
644-
return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va);
645-
}
646-
647-
return Sse.Add(Sse.Multiply(vm0, vm1), va);
619+
return va + (vm0 * vm1);
648620
}
649621

650622
/// <summary>

src/ImageSharp/Common/Helpers/SimdUtils.cs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

44
using System.Diagnostics;
55
using System.Numerics;
66
using System.Runtime.CompilerServices;
7-
using System.Runtime.InteropServices;
87
using System.Runtime.Intrinsics;
8+
using System.Runtime.Intrinsics.Arm;
99
using System.Runtime.Intrinsics.X86;
1010

1111
namespace SixLabors.ImageSharp;
@@ -36,30 +36,39 @@ internal static Vector4 PseudoRound(this Vector4 v)
3636

3737
/// <summary>
3838
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics.
39-
/// Source:
40-
/// <see>
41-
/// <cref>https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110</cref>
42-
/// </see>
4339
/// </summary>
4440
/// <param name="v">The vector</param>
4541
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4642
internal static Vector<float> FastRound(this Vector<float> v)
4743
{
48-
if (Avx2.IsSupported)
44+
// .NET9+ has a built-in method for this Vector.Round
45+
if (Avx2.IsSupported && Vector<float>.Count == Vector256<float>.Count)
4946
{
5047
ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v);
5148
Vector256<float> vRound = Avx.RoundToNearestInteger(v256);
5249
return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound);
5350
}
54-
else
51+
52+
if (Sse41.IsSupported && Vector<float>.Count == Vector128<float>.Count)
53+
{
54+
ref Vector128<float> v128 = ref Unsafe.As<Vector<float>, Vector128<float>>(ref v);
55+
Vector128<float> vRound = Sse41.RoundToNearestInteger(v128);
56+
return Unsafe.As<Vector128<float>, Vector<float>>(ref vRound);
57+
}
58+
59+
if (AdvSimd.IsSupported && Vector<float>.Count == Vector128<float>.Count)
5560
{
56-
var magic0 = new Vector<int>(int.MinValue); // 0x80000000
57-
var sgn0 = Vector.AsVectorSingle(magic0);
58-
var and0 = Vector.BitwiseAnd(sgn0, v);
59-
var or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f));
60-
var add0 = Vector.Add(v, or0);
61-
return Vector.Subtract(add0, or0);
61+
ref Vector128<float> v128 = ref Unsafe.As<Vector<float>, Vector128<float>>(ref v);
62+
Vector128<float> vRound = AdvSimd.RoundToNearest(v128);
63+
return Unsafe.As<Vector128<float>, Vector<float>>(ref vRound);
6264
}
65+
66+
// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11
67+
Vector<float> sign = v & new Vector<float>(-0F);
68+
Vector<float> val_2p23_f32 = sign | new Vector<float>(8388608F);
69+
70+
val_2p23_f32 = (v + val_2p23_f32) - val_2p23_f32;
71+
return val_2p23_f32 | sign;
6372
}
6473

6574
[Conditional("DEBUG")]

src/ImageSharp/Common/Helpers/Vector128Utilities.cs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,65 @@ public static Vector128<int> ConvertToInt32RoundToEven(Vector128<float> vector)
193193
return AdvSimd.ConvertToInt32RoundToEven(vector);
194194
}
195195

196-
Vector128<float> sign = vector & Vector128.Create(-0.0f);
197-
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608.0f);
196+
Vector128<float> sign = vector & Vector128.Create(-0F);
197+
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608F);
198198

199199
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32;
200200
return Vector128.ConvertToInt32(val_2p23_f32 | sign);
201201
}
202202

203+
/// <summary>
204+
/// Rounds all values in <paramref name="vector"/> to the nearest integer
205+
/// following <see cref="MidpointRounding.ToEven"/> semantics.
206+
/// </summary>
207+
/// <param name="vector">The vector</param>
208+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
209+
public static Vector128<float> RoundToNearestInteger(Vector128<float> vector)
210+
{
211+
if (Sse41.IsSupported)
212+
{
213+
return Sse41.RoundToNearestInteger(vector);
214+
}
215+
216+
if (AdvSimd.IsSupported)
217+
{
218+
return AdvSimd.RoundToNearest(vector);
219+
}
220+
221+
Vector128<float> sign = vector & Vector128.Create(-0F);
222+
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608F);
223+
224+
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32;
225+
return val_2p23_f32 | sign;
226+
}
227+
228+
/// <summary>
229+
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
230+
/// </summary>
231+
/// <remarks>ret = (vm0 * vm1) + va</remarks>
232+
/// <param name="va">The vector to add to the intermediate result.</param>
233+
/// <param name="vm0">The first vector to multiply.</param>
234+
/// <param name="vm1">The second vector to multiply.</param>
235+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
236+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
237+
public static Vector128<float> MultiplyAdd(
238+
Vector128<float> va,
239+
Vector128<float> vm0,
240+
Vector128<float> vm1)
241+
{
242+
if (Fma.IsSupported)
243+
{
244+
return Fma.MultiplyAdd(vm1, vm0, va);
245+
}
246+
247+
if (AdvSimd.IsSupported)
248+
{
249+
return AdvSimd.FusedMultiplyAdd(va, vm0, vm1);
250+
}
251+
252+
return va + (vm0 * vm1);
253+
}
254+
203255
/// <summary>
204256
/// Packs signed 16-bit integers to unsigned 8-bit integers and saturates.
205257
/// </summary>

src/ImageSharp/Common/Helpers/Vector256Utilities.cs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,55 @@ public static Vector256<int> ConvertToInt32RoundToEven(Vector256<float> vector)
103103
return Vector256.Create(lower, upper);
104104
}
105105

106-
Vector256<float> sign = vector & Vector256.Create(-0.0f);
107-
Vector256<float> val_2p23_f32 = sign | Vector256.Create(8388608.0f);
106+
Vector256<float> sign = vector & Vector256.Create(-0F);
107+
Vector256<float> val_2p23_f32 = sign | Vector256.Create(8388608F);
108108

109109
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32;
110110
return Vector256.ConvertToInt32(val_2p23_f32 | sign);
111111
}
112112

113+
/// <summary>
114+
/// Rounds all values in <paramref name="vector"/> to the nearest integer
115+
/// following <see cref="MidpointRounding.ToEven"/> semantics.
116+
/// </summary>
117+
/// <param name="vector">The vector</param>
118+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
119+
public static Vector256<float> RoundToNearestInteger(Vector256<float> vector)
120+
{
121+
if (Avx.IsSupported)
122+
{
123+
return Avx.RoundToNearestInteger(vector);
124+
}
125+
126+
Vector256<float> sign = vector & Vector256.Create(-0F);
127+
Vector256<float> val_2p23_f32 = sign | Vector256.Create(8388608F);
128+
129+
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32;
130+
return val_2p23_f32 | sign;
131+
}
132+
133+
/// <summary>
134+
/// Performs a multiplication and an addition of the <see cref="Vector256{Single}"/>.
135+
/// </summary>
136+
/// <remarks>ret = (vm0 * vm1) + va</remarks>
137+
/// <param name="va">The vector to add to the intermediate result.</param>
138+
/// <param name="vm0">The first vector to multiply.</param>
139+
/// <param name="vm1">The second vector to multiply.</param>
140+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
141+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
142+
public static Vector256<float> MultiplyAdd(
143+
Vector256<float> va,
144+
Vector256<float> vm0,
145+
Vector256<float> vm1)
146+
{
147+
if (Fma.IsSupported)
148+
{
149+
return Fma.MultiplyAdd(vm0, vm1, va);
150+
}
151+
152+
return va + (vm0 * vm1);
153+
}
154+
113155
[DoesNotReturn]
114156
private static void ThrowUnreachableException() => throw new UnreachableException();
115157
}

src/ImageSharp/Common/Helpers/Vector512Utilities.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,51 @@ public static Vector512<int> ConvertToInt32RoundToEven(Vector512<float> vector)
110110
return Vector512.ConvertToInt32(val_2p23_f32 | sign);
111111
}
112112

113+
/// <summary>
114+
/// Rounds all values in <paramref name="vector"/> to the nearest integer
115+
/// following <see cref="MidpointRounding.ToEven"/> semantics.
116+
/// </summary>
117+
/// <param name="vector">The vector</param>
118+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
119+
public static Vector512<float> RoundToNearestInteger(Vector512<float> vector)
120+
{
121+
if (Avx512F.IsSupported)
122+
{
123+
// imm8 = 0b1000:
124+
// imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers)
125+
// imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions)
126+
return Avx512F.RoundScale(vector, 0b0000_1000);
127+
}
128+
129+
Vector512<float> sign = vector & Vector512.Create(-0F);
130+
Vector512<float> val_2p23_f32 = sign | Vector512.Create(8388608F);
131+
132+
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32;
133+
return val_2p23_f32 | sign;
134+
}
135+
136+
/// <summary>
137+
/// Performs a multiplication and an addition of the <see cref="Vector512{Single}"/>.
138+
/// </summary>
139+
/// <remarks>ret = (vm0 * vm1) + va</remarks>
140+
/// <param name="va">The vector to add to the intermediate result.</param>
141+
/// <param name="vm0">The first vector to multiply.</param>
142+
/// <param name="vm1">The second vector to multiply.</param>
143+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
144+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
145+
public static Vector512<float> MultiplyAdd(
146+
Vector512<float> va,
147+
Vector512<float> vm0,
148+
Vector512<float> vm1)
149+
{
150+
if (Avx512F.IsSupported)
151+
{
152+
return Avx512F.FusedMultiplyAdd(vm0, vm1, va);
153+
}
154+
155+
return va + (vm0 * vm1);
156+
}
157+
113158
[DoesNotReturn]
114159
private static void ThrowUnreachableException() => throw new UnreachableException();
115160
}

src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ public CmykScalar(int precision)
1313
}
1414

1515
/// <inheritdoc/>
16-
public override void ConvertToRgbInplace(in ComponentValues values) =>
17-
ConvertToRgbInplace(values, this.MaximumValue);
16+
public override void ConvertToRgbInPlace(in ComponentValues values) =>
17+
ConvertToRgbInPlace(values, this.MaximumValue);
1818

1919
/// <inheritdoc/>
20-
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b)
21-
=> ConvertFromRgb(values, this.MaximumValue, r, g, b);
20+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
21+
=> ConvertFromRgb(values, this.MaximumValue, rLane, gLane, bLane);
2222

23-
public static void ConvertToRgbInplace(in ComponentValues values, float maxValue)
23+
public static void ConvertToRgbInPlace(in ComponentValues values, float maxValue)
2424
{
2525
Span<float> c0 = values.Component0;
2626
Span<float> c1 = values.Component1;
@@ -42,7 +42,7 @@ public static void ConvertToRgbInplace(in ComponentValues values, float maxValue
4242
}
4343
}
4444

45-
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b)
45+
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane)
4646
{
4747
Span<float> c = values.Component0;
4848
Span<float> m = values.Component1;
@@ -51,9 +51,9 @@ public static void ConvertFromRgb(in ComponentValues values, float maxValue, Spa
5151

5252
for (int i = 0; i < c.Length; i++)
5353
{
54-
float ctmp = 255f - r[i];
55-
float mtmp = 255f - g[i];
56-
float ytmp = 255f - b[i];
54+
float ctmp = 255f - rLane[i];
55+
float mtmp = 255f - gLane[i];
56+
float ytmp = 255f - bLane[i];
5757
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp);
5858

5959
if (ktmp >= 255f)

0 commit comments

Comments
 (0)