mirror of https://github.com/SixLabors/ImageSharp
28 changed files with 781 additions and 317 deletions
@ -0,0 +1,417 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized static methods for trigonometric, logarithmic,
|
|||
/// and other common mathematical functions.
|
|||
/// </summary>
|
|||
internal static class Numerics |
|||
{ |
|||
/// <summary>
|
|||
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
|||
/// </summary>
|
|||
public static int GreatestCommonDivisor(int a, int b) |
|||
{ |
|||
while (b != 0) |
|||
{ |
|||
int temp = b; |
|||
b = a % b; |
|||
a = temp; |
|||
} |
|||
|
|||
return a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine the Least Common Multiple (LCM) of two numbers.
|
|||
/// </summary>
|
|||
public static int LeastCommonMultiple(int a, int b) |
|||
{ |
|||
// https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
|
|||
return (a / GreatestCommonDivisor(a, b)) * b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 2
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo2(int x) => x & 1; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 4
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo4(int x) => x & 3; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 8
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo8(int x) => x & 7; |
|||
|
|||
/// <summary>
|
|||
/// Fast (x mod m) calculator, with the restriction that
|
|||
/// <paramref name="m"/> should be power of 2.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int ModuloP2(int x, int m) => x & (m - 1); |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a 32-bit signed integer.
|
|||
/// Uses bit shifting to speed up the operation compared to <see cref="Math"/>.
|
|||
/// </summary>
|
|||
/// <param name="x">
|
|||
/// A number that is greater than <see cref="int.MinValue"/>, but less than
|
|||
/// or equal to <see cref="int.MaxValue"/>
|
|||
/// </param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Abs(int x) |
|||
{ |
|||
int y = x >> 31; |
|||
return (x ^ y) - y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 2
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow2(float x) => x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 3
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow3(float x) => x * x * x; |
|||
|
|||
/// <summary>
|
|||
/// Implementation of 1D Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="x">The x provided to G(x).</param>
|
|||
/// <param name="sigma">The spread of the blur.</param>
|
|||
/// <returns>The Gaussian G(x)</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Gaussian(float x, float sigma) |
|||
{ |
|||
const float Numerator = 1.0f; |
|||
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; |
|||
|
|||
float exponentNumerator = -x * x; |
|||
float exponentDenominator = 2 * Pow2(sigma); |
|||
|
|||
float left = Numerator / denominator; |
|||
float right = MathF.Exp(exponentNumerator / exponentDenominator); |
|||
|
|||
return left * right; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the result of a normalized sine cardinal function for the given value.
|
|||
/// SinC(x) = sin(pi*x)/(pi*x).
|
|||
/// </summary>
|
|||
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
|||
/// <returns>
|
|||
/// The sine cardinal of <paramref name="f" />.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float SinC(float f) |
|||
{ |
|||
if (MathF.Abs(f) > Constants.Epsilon) |
|||
{ |
|||
f *= MathF.PI; |
|||
float result = MathF.Sin(f) / f; |
|||
return MathF.Abs(result) < Constants.Epsilon ? 0F : result; |
|||
} |
|||
|
|||
return 1F; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="byte"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte Clamp(byte value, byte min, byte max) |
|||
{ |
|||
// Order is important here as someone might set min to higher than max.
|
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static uint Clamp(uint value, uint min, uint max) |
|||
{ |
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Clamp(int value, int min, int max) |
|||
{ |
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="float"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Clamp(float value, float min, float max) |
|||
{ |
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="double"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static double Clamp(double value, double min, double max) |
|||
{ |
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<byte> span, byte min, byte max) |
|||
{ |
|||
int reduced = ClampReduce(span, min, max); |
|||
|
|||
if (reduced > 0) |
|||
{ |
|||
for (int i = reduced; i < span.Length; i++) |
|||
{ |
|||
ref byte v = ref span[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<uint> span, uint min, uint max) |
|||
{ |
|||
int reduced = ClampReduce(span, min, max); |
|||
|
|||
if (reduced > 0) |
|||
{ |
|||
for (int i = reduced; i < span.Length; i++) |
|||
{ |
|||
ref uint v = ref span[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<int> span, int min, int max) |
|||
{ |
|||
int reduced = ClampReduce(span, min, max); |
|||
|
|||
if (reduced > 0) |
|||
{ |
|||
for (int i = reduced; i < span.Length; i++) |
|||
{ |
|||
ref int v = ref span[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<float> span, float min, float max) |
|||
{ |
|||
int reduced = ClampReduce(span, min, max); |
|||
|
|||
if (reduced > 0) |
|||
{ |
|||
for (int i = reduced; i < span.Length; i++) |
|||
{ |
|||
ref float v = ref span[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<double> span, double min, double max) |
|||
{ |
|||
int reduced = ClampReduce(span, min, max); |
|||
|
|||
if (reduced > 0) |
|||
{ |
|||
for (int i = reduced; i < span.Length; i++) |
|||
{ |
|||
ref double v = ref span[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static int ClampReduce<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count) |
|||
{ |
|||
int remainder = ModuloP2(span.Length, Vector<T>.Count); |
|||
int adjustedCount = span.Length - remainder; |
|||
|
|||
if (adjustedCount > 0) |
|||
{ |
|||
ClampImpl(span.Slice(0, adjustedCount), min, max); |
|||
} |
|||
|
|||
return adjustedCount; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void ClampImpl<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
ref T sRef = ref MemoryMarshal.GetReference(span); |
|||
ref Vector<T> vsBase = ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span)); |
|||
var vmin = new Vector<T>(min); |
|||
var vmax = new Vector<T>(max); |
|||
|
|||
int n = span.Length / Vector<T>.Count; |
|||
int m = Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
|||
ref Vector<T> vs1 = ref Unsafe.Add(ref vs0, 1); |
|||
ref Vector<T> vs2 = ref Unsafe.Add(ref vs0, 2); |
|||
ref Vector<T> vs3 = ref Unsafe.Add(ref vs0, 3); |
|||
|
|||
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
|||
vs1 = Vector.Min(Vector.Max(vmin, vs1), vmax); |
|||
vs2 = Vector.Min(Vector.Max(vmin, vs2), vmax); |
|||
vs3 = Vector.Min(Vector.Max(vmin, vs3), vmax); |
|||
} |
|||
|
|||
if (m > 0) |
|||
{ |
|||
for (int i = u; i < n; i++) |
|||
{ |
|||
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
|||
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath |
|||
{ |
|||
public class ClampSpan |
|||
{ |
|||
private static readonly int[] A = new int[2048]; |
|||
private static readonly int[] B = new int[2048]; |
|||
|
|||
public void Setup() |
|||
{ |
|||
var r = new Random(); |
|||
|
|||
for (int i = 0; i < A.Length; i++) |
|||
{ |
|||
int x = r.Next(); |
|||
A[i] = x; |
|||
B[i] = x; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void ClampNoIntrinsics() |
|||
{ |
|||
for (int i = 0; i < A.Length; i++) |
|||
{ |
|||
ref int x = ref A[i]; |
|||
x = x.Clamp(64, 128); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void ClampVectorIntrinsics() |
|||
{ |
|||
Numerics.Clamp(B, 64, 128); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,265 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Helpers |
|||
{ |
|||
public class NumericsTests |
|||
{ |
|||
public delegate void SpanAction<T, in TArg, in TArg1>(Span<T> span, TArg arg, TArg1 arg1); |
|||
|
|||
[Theory] |
|||
[InlineData(0)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
[InlineData(3)] |
|||
[InlineData(4)] |
|||
[InlineData(100)] |
|||
[InlineData(123)] |
|||
[InlineData(53436353)] |
|||
public void Modulo2(int x) |
|||
{ |
|||
int actual = Numerics.Modulo2(x); |
|||
Assert.Equal(x % 2, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(0)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
[InlineData(3)] |
|||
[InlineData(4)] |
|||
[InlineData(100)] |
|||
[InlineData(123)] |
|||
[InlineData(53436353)] |
|||
public void Modulo4(int x) |
|||
{ |
|||
int actual = Numerics.Modulo4(x); |
|||
Assert.Equal(x % 4, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(0)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
[InlineData(6)] |
|||
[InlineData(7)] |
|||
[InlineData(8)] |
|||
[InlineData(100)] |
|||
[InlineData(123)] |
|||
[InlineData(53436353)] |
|||
[InlineData(975)] |
|||
public void Modulo8(int x) |
|||
{ |
|||
int actual = Numerics.Modulo8(x); |
|||
Assert.Equal(x % 8, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(0, 2)] |
|||
[InlineData(1, 2)] |
|||
[InlineData(2, 2)] |
|||
[InlineData(0, 4)] |
|||
[InlineData(3, 4)] |
|||
[InlineData(5, 4)] |
|||
[InlineData(5, 8)] |
|||
[InlineData(8, 8)] |
|||
[InlineData(8, 16)] |
|||
[InlineData(15, 16)] |
|||
[InlineData(17, 16)] |
|||
[InlineData(17, 32)] |
|||
[InlineData(31, 32)] |
|||
[InlineData(32, 32)] |
|||
[InlineData(33, 32)] |
|||
public void Modulo2P(int x, int m) |
|||
{ |
|||
int actual = Numerics.ModuloP2(x, m); |
|||
Assert.Equal(x % m, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-5)] |
|||
[InlineData(-17)] |
|||
[InlineData(-12856)] |
|||
[InlineData(-32)] |
|||
[InlineData(-7425)] |
|||
[InlineData(5)] |
|||
[InlineData(17)] |
|||
[InlineData(12856)] |
|||
[InlineData(32)] |
|||
[InlineData(7425)] |
|||
public void Abs(int x) |
|||
{ |
|||
int expected = Math.Abs(x); |
|||
Assert.Equal(expected, Numerics.Abs(x)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-5)] |
|||
[InlineData(-17)] |
|||
[InlineData(-12856)] |
|||
[InlineData(-32)] |
|||
[InlineData(-7425)] |
|||
[InlineData(5)] |
|||
[InlineData(17)] |
|||
[InlineData(12856)] |
|||
[InlineData(32)] |
|||
[InlineData(7425)] |
|||
public void Pow2(float x) |
|||
{ |
|||
float expected = (float)Math.Pow(x, 2); |
|||
Assert.Equal(expected, Numerics.Pow2(x)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-5)] |
|||
[InlineData(-17)] |
|||
[InlineData(-12856)] |
|||
[InlineData(-32)] |
|||
[InlineData(5)] |
|||
[InlineData(17)] |
|||
[InlineData(12856)] |
|||
[InlineData(32)] |
|||
public void Pow3(float x) |
|||
{ |
|||
float expected = (float)Math.Pow(x, 3); |
|||
Assert.Equal(expected, Numerics.Pow3(x)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(1, 1, 1)] |
|||
[InlineData(1, 42, 1)] |
|||
[InlineData(10, 8, 2)] |
|||
[InlineData(12, 18, 6)] |
|||
[InlineData(4536, 1000, 8)] |
|||
[InlineData(1600, 1024, 64)] |
|||
public void GreatestCommonDivisor(int a, int b, int expected) |
|||
{ |
|||
int actual = Numerics.GreatestCommonDivisor(a, b); |
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(1, 1, 1)] |
|||
[InlineData(1, 42, 42)] |
|||
[InlineData(3, 4, 12)] |
|||
[InlineData(6, 4, 12)] |
|||
[InlineData(1600, 1024, 25600)] |
|||
[InlineData(3264, 100, 81600)] |
|||
public void LeastCommonMultiple(int a, int b, int expected) |
|||
{ |
|||
int actual = Numerics.LeastCommonMultiple(a, b); |
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(64, 36, 96)] |
|||
[InlineData(128, 16, 196)] |
|||
[InlineData(567, 18, 142)] |
|||
[InlineData(1024, 0, 255)] |
|||
public void ClampByte(int length, byte min, byte max) |
|||
{ |
|||
TestClampSpan( |
|||
length, |
|||
min, |
|||
max, |
|||
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
|||
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(64, 36, 96)] |
|||
[InlineData(128, 16, 196)] |
|||
[InlineData(567, 18, 142)] |
|||
[InlineData(1024, 0, 255)] |
|||
public void ClampInt(int length, int min, int max) |
|||
{ |
|||
TestClampSpan( |
|||
length, |
|||
min, |
|||
max, |
|||
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
|||
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(64, 36, 96)] |
|||
[InlineData(128, 16, 196)] |
|||
[InlineData(567, 18, 142)] |
|||
[InlineData(1024, 0, 255)] |
|||
public void ClampUInt(int length, uint min, uint max) |
|||
{ |
|||
TestClampSpan( |
|||
length, |
|||
min, |
|||
max, |
|||
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
|||
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(64, 36, 96)] |
|||
[InlineData(128, 16, 196)] |
|||
[InlineData(567, 18, 142)] |
|||
[InlineData(1024, 0, 255)] |
|||
public void ClampFloat(int length, float min, float max) |
|||
{ |
|||
TestClampSpan( |
|||
length, |
|||
min, |
|||
max, |
|||
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
|||
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(64, 36, 96)] |
|||
[InlineData(128, 16, 196)] |
|||
[InlineData(567, 18, 142)] |
|||
[InlineData(1024, 0, 255)] |
|||
public void ClampDouble(int length, double min, double max) |
|||
{ |
|||
TestClampSpan( |
|||
length, |
|||
min, |
|||
max, |
|||
(s, m1, m2) => Numerics.Clamp(s, m1, m2), |
|||
(v, m1, m2) => Numerics.Clamp(v, m1, m2)); |
|||
} |
|||
|
|||
private static void TestClampSpan<T>( |
|||
int length, |
|||
T min, |
|||
T max, |
|||
SpanAction<T, T, T> clampAction, |
|||
Func<T, T, T, T> refClampFunc) |
|||
where T : unmanaged, IComparable<T> |
|||
{ |
|||
Span<T> actual = new T[length]; |
|||
|
|||
var r = new Random(); |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
actual[i] = (T)Convert.ChangeType(r.Next(byte.MinValue, byte.MaxValue), typeof(T)); |
|||
} |
|||
|
|||
Span<T> expected = new T[length]; |
|||
actual.CopyTo(expected); |
|||
|
|||
for (int i = 0; i < expected.Length; i++) |
|||
{ |
|||
ref T v = ref expected[i]; |
|||
v = refClampFunc(v, min, max); |
|||
} |
|||
|
|||
clampAction(actual, min, max); |
|||
|
|||
for (int i = 0; i < expected.Length; i++) |
|||
{ |
|||
Assert.Equal(expected[i], actual[i]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue