-
-
Notifications
You must be signed in to change notification settings - Fork 870
Modernize JPEG Color Converters #2917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
aebe375
cd3aa18
959e600
597bc0c
3b430e1
87438a3
80fee7f
57614df
71ca954
277cf61
5b94795
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
// Copyright (c) Six Labors. | ||
// Copyright (c) Six Labors. | ||
// Licensed under the Six Labors Split License. | ||
|
||
using System.Diagnostics; | ||
using System.Numerics; | ||
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; | ||
|
@@ -36,30 +36,39 @@ internal static Vector4 PseudoRound(this Vector4 v) | |
|
||
/// <summary> | ||
/// Rounds all values in 'v' to the nearest integer following <see cref="MidpointRounding.ToEven"/> semantics. | ||
/// Source: | ||
/// <see> | ||
/// <cref>https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110</cref> | ||
/// </see> | ||
/// </summary> | ||
/// <param name="v">The vector</param> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal static Vector<float> FastRound(this Vector<float> v) | ||
{ | ||
if (Avx2.IsSupported) | ||
// .NET9+ has a built-in method for this Vector.Round | ||
if (Avx2.IsSupported && Vector<float>.Count == Vector256<float>.Count) | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know whether this is ever false. But I wanted the check to be a little stricter given for the future potential of 512 supported There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be false even when Notably you have .NET 9+ defines |
||
ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v); | ||
Vector256<float> vRound = Avx.RoundToNearestInteger(v256); | ||
return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound); | ||
} | ||
else | ||
|
||
if (Sse41.IsSupported && Vector<float>.Count == Vector128<float>.Count) | ||
{ | ||
ref Vector128<float> v128 = ref Unsafe.As<Vector<float>, Vector128<float>>(ref v); | ||
Vector128<float> vRound = Sse41.RoundToNearestInteger(v128); | ||
return Unsafe.As<Vector128<float>, Vector<float>>(ref vRound); | ||
} | ||
|
||
if (AdvSimd.IsSupported && Vector<float>.Count == Vector128<float>.Count) | ||
{ | ||
var magic0 = new Vector<int>(int.MinValue); // 0x80000000 | ||
var sgn0 = Vector.AsVectorSingle(magic0); | ||
var and0 = Vector.BitwiseAnd(sgn0, v); | ||
var or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f)); | ||
var add0 = Vector.Add(v, or0); | ||
return Vector.Subtract(add0, or0); | ||
ref Vector128<float> v128 = ref Unsafe.As<Vector<float>, Vector128<float>>(ref v); | ||
Vector128<float> vRound = AdvSimd.RoundToNearest(v128); | ||
return Unsafe.As<Vector128<float>, Vector<float>>(ref vRound); | ||
} | ||
|
||
// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L11 | ||
Vector<float> sign = v & new Vector<float>(-0F); | ||
Vector<float> val_2p23_f32 = sign | new Vector<float>(8388608F); | ||
|
||
val_2p23_f32 = (v + val_2p23_f32) - val_2p23_f32; | ||
return val_2p23_f32 | sign; | ||
} | ||
|
||
[Conditional("DEBUG")] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,13 +193,65 @@ public static Vector128<int> ConvertToInt32RoundToEven(Vector128<float> vector) | |
return AdvSimd.ConvertToInt32RoundToEven(vector); | ||
} | ||
|
||
Vector128<float> sign = vector & Vector128.Create(-0.0f); | ||
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608.0f); | ||
Vector128<float> sign = vector & Vector128.Create(-0F); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No general helper exists for .NET 9+ here... It's something we could probably pattern recognize as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have a look at creating an issue. Thanks. |
||
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608F); | ||
|
||
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; | ||
return Vector128.ConvertToInt32(val_2p23_f32 | sign); | ||
} | ||
|
||
/// <summary> | ||
/// Rounds all values in <paramref name="vector"/> to the nearest integer | ||
/// following <see cref="MidpointRounding.ToEven"/> semantics. | ||
/// </summary> | ||
/// <param name="vector">The vector</param> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public static Vector128<float> RoundToNearestInteger(Vector128<float> vector) | ||
{ | ||
if (Sse41.IsSupported) | ||
{ | ||
return Sse41.RoundToNearestInteger(vector); | ||
} | ||
|
||
if (AdvSimd.IsSupported) | ||
{ | ||
return AdvSimd.RoundToNearest(vector); | ||
} | ||
|
||
Vector128<float> sign = vector & Vector128.Create(-0F); | ||
Vector128<float> val_2p23_f32 = sign | Vector128.Create(8388608F); | ||
|
||
val_2p23_f32 = (vector + val_2p23_f32) - val_2p23_f32; | ||
return val_2p23_f32 | sign; | ||
} | ||
|
||
/// <summary> | ||
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>. | ||
/// </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(MethodImplOptions.AggressiveInlining)] | ||
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.FusedMultiplyAdd(va, vm0, vm1); | ||
} | ||
|
||
return va + (vm0 * vm1); | ||
} | ||
|
||
/// <summary> | ||
/// Packs signed 16-bit integers to unsigned 8-bit integers and saturates. | ||
/// </summary> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually intend on removing this copy of the method in future PRs. Doing so in this one would have touched too many unrelated files.