Skip to content

Commit 959e600

Browse files
Begin re-implementing converters
1 parent cd3aa18 commit 959e600

19 files changed

+768
-35
lines changed

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

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

619-
return Avx.Add(Avx.Multiply(vm0, vm1), va);
619+
if (Avx.IsSupported)
620+
{
621+
return Avx.Add(Avx.Multiply(vm0, vm1), va);
622+
}
623+
624+
return va + (vm0 * vm1);
620625
}
621626

622627
/// <summary>
@@ -644,7 +649,12 @@ public static Vector128<float> MultiplyAdd(
644649
return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va);
645650
}
646651

647-
return Sse.Add(Sse.Multiply(vm0, vm1), va);
652+
if (Sse.IsSupported)
653+
{
654+
return Sse.Add(Sse.Multiply(vm0, vm1), va);
655+
}
656+
657+
return va + (vm0 * vm1);
648658
}
649659

650660
/// <summary>

src/ImageSharp/Common/Helpers/Vector128Utilities.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,70 @@ 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.Add(AdvSimd.Multiply(vm0, vm1), va);
250+
}
251+
252+
if (Sse.IsSupported)
253+
{
254+
return Sse.Add(Sse.Multiply(vm0, vm1), va);
255+
}
256+
257+
return va + (vm0 * vm1);
258+
}
259+
203260
/// <summary>
204261
/// Packs signed 16-bit integers to unsigned 8-bit integers and saturates.
205262
/// </summary>

src/ImageSharp/Common/Helpers/Vector256Utilities.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,60 @@ 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(vm1, vm0, va);
150+
}
151+
152+
if (Avx.IsSupported)
153+
{
154+
return Avx.Add(Avx.Multiply(vm0, vm1), va);
155+
}
156+
157+
return va + (vm0 * vm1);
158+
}
159+
113160
[DoesNotReturn]
114161
private static void ThrowUnreachableException() => throw new UnreachableException();
115162
}

src/ImageSharp/Common/Helpers/Vector512Utilities.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,43 @@ 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) => va + (vm0 * vm1);
149+
113150
[DoesNotReturn]
114151
private static void ThrowUnreachableException() => throw new UnreachableException();
115152
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public CmykVector(int precision)
1717
}
1818

1919
/// <inheritdoc/>
20-
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
20+
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
2121
{
2222
ref Vector<float> cBase =
2323
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@@ -46,7 +46,7 @@ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
4646
}
4747

4848
/// <inheritdoc/>
49-
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
49+
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
5050
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue);
5151

5252
/// <inheritdoc/>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public GrayScaleVector(int precision)
1717
}
1818

1919
/// <inheritdoc/>
20-
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
20+
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
2121
{
2222
ref Vector<float> cBase =
2323
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@@ -33,7 +33,7 @@ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
3333
}
3434

3535
/// <inheritdoc/>
36-
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
36+
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
3737
=> GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue);
3838

3939
/// <inheritdoc/>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public RgbVector(int precision)
1717
}
1818

1919
/// <inheritdoc/>
20-
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
20+
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
2121
{
2222
ref Vector<float> rBase =
2323
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@@ -41,7 +41,7 @@ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
4141
}
4242

4343
/// <inheritdoc/>
44-
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
44+
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
4545
=> RgbScalar.ConvertToRgbInplace(values, this.MaximumValue);
4646

4747
/// <inheritdoc/>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public YCbCrVector(int precision)
1818
}
1919

2020
/// <inheritdoc/>
21-
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
21+
protected override void ConvertToRgbInPlaceVectorized(in ComponentValues values)
2222
{
2323
ref Vector<float> c0Base =
2424
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@@ -69,7 +69,7 @@ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
6969
}
7070

7171
/// <inheritdoc/>
72-
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
72+
protected override void ConvertToRgbInPlaceScalarRemainder(in ComponentValues values)
7373
=> YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue);
7474

7575
/// <inheritdoc/>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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 SixLabors.ImageSharp.Common.Helpers;
8+
9+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
10+
11+
internal abstract partial class JpegColorConverterBase
12+
{
13+
internal sealed class YCbCrVector128 : JpegColorConverterVector128
14+
{
15+
public YCbCrVector128(int precision)
16+
: base(JpegColorSpace.YCbCr, 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+
30+
Vector128<float> chromaOffset = Vector128.Create(-this.HalfValue);
31+
Vector128<float> scale = Vector128.Create(1 / this.MaximumValue);
32+
Vector128<float> rCrMult = Vector128.Create(YCbCrScalar.RCrMult);
33+
Vector128<float> gCbMult = Vector128.Create(-YCbCrScalar.GCbMult);
34+
Vector128<float> gCrMult = Vector128.Create(-YCbCrScalar.GCrMult);
35+
Vector128<float> bCbMult = Vector128.Create(YCbCrScalar.BCbMult);
36+
37+
// Walking 8 elements at one step:
38+
nuint n = values.Component0.Vector128Count<float>();
39+
for (nuint i = 0; i < n; i++)
40+
{
41+
// y = yVals[i];
42+
// cb = cbVals[i] - 128F;
43+
// cr = crVals[i] - 128F;
44+
ref Vector128<float> c0 = ref Unsafe.Add(ref c0Base, i);
45+
ref Vector128<float> c1 = ref Unsafe.Add(ref c1Base, i);
46+
ref Vector128<float> c2 = ref Unsafe.Add(ref c2Base, i);
47+
48+
Vector128<float> y = c0;
49+
Vector128<float> cb = c1 + chromaOffset;
50+
Vector128<float> cr = c2 + chromaOffset;
51+
52+
// r = y + (1.402F * cr);
53+
// g = y - (0.344136F * cb) - (0.714136F * cr);
54+
// b = y + (1.772F * cb);
55+
Vector128<float> r = Vector128Utilities.MultiplyAdd(y, cr, rCrMult);
56+
Vector128<float> g = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
57+
Vector128<float> b = Vector128Utilities.MultiplyAdd(y, cb, bCbMult);
58+
59+
r = Vector128Utilities.RoundToNearestInteger(r) * scale;
60+
g = Vector128Utilities.RoundToNearestInteger(g) * scale;
61+
b = Vector128Utilities.RoundToNearestInteger(b) * scale;
62+
63+
c0 = r;
64+
c1 = g;
65+
c2 = b;
66+
}
67+
}
68+
69+
/// <inheritdoc/>
70+
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane)
71+
{
72+
ref Vector128<float> destY =
73+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component0));
74+
ref Vector128<float> destCb =
75+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component1));
76+
ref Vector128<float> destCr =
77+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(values.Component2));
78+
79+
ref Vector128<float> srcR =
80+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(rLane));
81+
ref Vector128<float> srcG =
82+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(gLane));
83+
ref Vector128<float> srcB =
84+
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(bLane));
85+
86+
Vector128<float> chromaOffset = Vector128.Create(this.HalfValue);
87+
Vector128<float> f0299 = Vector128.Create(0.299f);
88+
Vector128<float> f0587 = Vector128.Create(0.587f);
89+
Vector128<float> f0114 = Vector128.Create(0.114f);
90+
Vector128<float> fn0168736 = Vector128.Create(-0.168736f);
91+
Vector128<float> fn0331264 = Vector128.Create(-0.331264f);
92+
Vector128<float> fn0418688 = Vector128.Create(-0.418688f);
93+
Vector128<float> fn0081312F = Vector128.Create(-0.081312F);
94+
Vector128<float> f05 = Vector128.Create(0.5f);
95+
96+
nuint n = values.Component0.Vector128Count<float>();
97+
for (nuint i = 0; i < n; i++)
98+
{
99+
Vector128<float> r = Unsafe.Add(ref srcR, i);
100+
Vector128<float> g = Unsafe.Add(ref srcG, i);
101+
Vector128<float> b = Unsafe.Add(ref srcB, i);
102+
103+
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
104+
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
105+
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
106+
Vector128<float> y = Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
107+
Vector128<float> cb = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
108+
Vector128<float> cr = chromaOffset + Vector128Utilities.MultiplyAdd(Vector128Utilities.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
109+
110+
Unsafe.Add(ref destY, i) = y;
111+
Unsafe.Add(ref destCb, i) = cb;
112+
Unsafe.Add(ref destCr, i) = cr;
113+
}
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)