diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
index 719565fd81..5cd89abfd6 100644
--- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the linear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
- => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
+ => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : Numerics.Pow3((channel + 0.16F) / 1.16F);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
index 31c3f46330..34354efe54 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
@@ -25,11 +25,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float fx = (a / 500F) + fy;
float fz = fy - (b / 200F);
- float fx3 = ImageMaths.Pow3(fx);
- float fz3 = ImageMaths.Pow3(fz);
+ float fx3 = Numerics.Pow3(fx);
+ float fz3 = Numerics.Pow3(fz);
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
- float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
+ float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
index 7f15fc77d8..12c65105fc 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float v0 = ComputeV0(input.WhitePoint);
float y = l > CieConstants.Kappa * CieConstants.Epsilon
- ? ImageMaths.Pow3((l + 16) / 116)
+ ? Numerics.Pow3((l + 16) / 116)
: l / CieConstants.Kappa;
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3;
@@ -71,4 +71,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static float ComputeV0(in CieXyz input)
=> (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
index 4c3cdba224..f120d6f3dd 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
- float pow = ImageMaths.Pow2(l / 100F);
+ float pow = Numerics.Pow2(l / 100F);
float sqrtPow = MathF.Sqrt(pow);
float y = pow * yn;
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index d24230fe18..55ac6f8227 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -4,13 +4,12 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
-
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
- /// Provides common mathematical methods.
+ /// Provides common mathematical methods used for image processing.
///
internal static class ImageMaths
{
@@ -108,85 +107,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);
- ///
- /// Determine the Greatest CommonDivisor (GCD) of two numbers.
- ///
- public static int GreatestCommonDivisor(int a, int b)
- {
- while (b != 0)
- {
- int temp = b;
- b = a % b;
- a = temp;
- }
-
- return a;
- }
-
- ///
- /// Determine the Least Common Multiple (LCM) of two numbers.
- ///
- 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;
- }
-
- ///
- /// Calculates % 2
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Modulo2(int x) => x & 1;
-
- ///
- /// Calculates % 4
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Modulo4(int x) => x & 3;
-
- ///
- /// Calculates % 8
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Modulo8(int x) => x & 7;
-
- ///
- /// Fast (x mod m) calculator, with the restriction that
- /// should be power of 2.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int ModuloP2(int x, int m) => x & (m - 1);
-
- ///
- /// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
- ///
- ///
- /// A number that is greater than , but less than or equal to
- ///
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int FastAbs(int x)
- {
- int y = x >> 31;
- return (x ^ y) - y;
- }
-
- ///
- /// Returns a specified number raised to the power of 2
- ///
- /// A single-precision floating-point number
- /// The number raised to the power of 2.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Pow2(float x) => x * x;
-
- ///
- /// Returns a specified number raised to the power of 3
- ///
- /// A single-precision floating-point number
- /// The number raised to the power of 3.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Pow3(float x) => x * x * x;
-
///
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
@@ -206,48 +126,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth;
- ///
- /// Implementation of 1D Gaussian G(x) function
- ///
- /// The x provided to G(x).
- /// The spread of the blur.
- /// The Gaussian G(x)
- [MethodImpl(InliningOptions.ShortMethod)]
- 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;
- }
-
- ///
- /// Returns the result of a normalized sine cardinal function for the given value.
- /// SinC(x) = sin(pi*x)/(pi*x).
- ///
- /// A single-precision floating-point number to calculate the result for.
- ///
- /// The sine cardinal of .
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- 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;
- }
-
///
/// Gets the bounding from the given points.
///
diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs
new file mode 100644
index 0000000000..d4dfa12024
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Numerics.cs
@@ -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
+{
+ ///
+ /// Provides optimized static methods for trigonometric, logarithmic,
+ /// and other common mathematical functions.
+ ///
+ internal static class Numerics
+ {
+ ///
+ /// Determine the Greatest CommonDivisor (GCD) of two numbers.
+ ///
+ public static int GreatestCommonDivisor(int a, int b)
+ {
+ while (b != 0)
+ {
+ int temp = b;
+ b = a % b;
+ a = temp;
+ }
+
+ return a;
+ }
+
+ ///
+ /// Determine the Least Common Multiple (LCM) of two numbers.
+ ///
+ 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;
+ }
+
+ ///
+ /// Calculates % 2
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo2(int x) => x & 1;
+
+ ///
+ /// Calculates % 4
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo4(int x) => x & 3;
+
+ ///
+ /// Calculates % 8
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo8(int x) => x & 7;
+
+ ///
+ /// Fast (x mod m) calculator, with the restriction that
+ /// should be power of 2.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ModuloP2(int x, int m) => x & (m - 1);
+
+ ///
+ /// Returns the absolute value of a 32-bit signed integer.
+ /// Uses bit shifting to speed up the operation compared to .
+ ///
+ ///
+ /// A number that is greater than , but less than
+ /// or equal to
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Abs(int x)
+ {
+ int y = x >> 31;
+ return (x ^ y) - y;
+ }
+
+ ///
+ /// Returns a specified number raised to the power of 2
+ ///
+ /// A single-precision floating-point number
+ /// The number raised to the power of 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Pow2(float x) => x * x;
+
+ ///
+ /// Returns a specified number raised to the power of 3
+ ///
+ /// A single-precision floating-point number
+ /// The number raised to the power of 3.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Pow3(float x) => x * x * x;
+
+ ///
+ /// Implementation of 1D Gaussian G(x) function
+ ///
+ /// The x provided to G(x).
+ /// The spread of the blur.
+ /// The Gaussian G(x)
+ [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;
+ }
+
+ ///
+ /// Returns the result of a normalized sine cardinal function for the given value.
+ /// SinC(x) = sin(pi*x)/(pi*x).
+ ///
+ /// A single-precision floating-point number to calculate the result for.
+ ///
+ /// The sine cardinal of .
+ ///
+ [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;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [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;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [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;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [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;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [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;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [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;
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span 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);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span 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);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span 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);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span 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);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span 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(Span span, T min, T max)
+ where T : unmanaged
+ {
+ if (Vector.IsHardwareAccelerated && span.Length >= Vector.Count)
+ {
+ int remainder = ModuloP2(span.Length, Vector.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(Span span, T min, T max)
+ where T : unmanaged
+ {
+ ref T sRef = ref MemoryMarshal.GetReference(span);
+ ref Vector vsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(span));
+ var vmin = new Vector(min);
+ var vmax = new Vector(max);
+
+ int n = span.Length / Vector.Count;
+ int m = Modulo4(n);
+ int u = n - m;
+
+ for (int i = 0; i < u; i += 4)
+ {
+ ref Vector vs0 = ref Unsafe.Add(ref vsBase, i);
+ ref Vector vs1 = ref Unsafe.Add(ref vs0, 1);
+ ref Vector vs2 = ref Unsafe.Add(ref vs0, 2);
+ ref Vector 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 vs0 = ref Unsafe.Add(ref vsBase, i);
+ vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax);
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
index 86e4174f11..3ecad3c5d9 100644
--- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp
ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 4;
- int m = ImageMaths.Modulo4(n);
+ int m = Numerics.Modulo4(n);
int u = n - m;
ref uint sLoopEnd = ref Unsafe.Add(ref sBase, u);
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
index de6990db5b..75555f88a5 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.Modulo8(source.Length);
+ int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.Modulo8(source.Length);
+ int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index bd35d1583e..0abc0e26da 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count);
+ int remainder = Numerics.ModuloP2(source.Length, Vector.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count);
+ int remainder = Numerics.ModuloP2(source.Length, Vector.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
index 1e89aaeb83..a97510113c 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
- int remainder = ImageMaths.Modulo4(source.Length);
+ int remainder = Numerics.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
- int remainder = ImageMaths.Modulo4(source.Length);
+ int remainder = Numerics.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
index 2ea7f2c9bd..b760301167 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp
if (Avx.IsSupported || Sse.IsSupported)
{
int remainder = Avx.IsSupported
- ? ImageMaths.ModuloP2(source.Length, Vector256.Count)
- : ImageMaths.ModuloP2(source.Length, Vector128.Count);
+ ? Numerics.ModuloP2(source.Length, Vector256.Count)
+ : Numerics.ModuloP2(source.Length, Vector128.Count);
int adjustedCount = source.Length - remainder;
@@ -72,8 +72,8 @@ namespace SixLabors.ImageSharp
if (Avx2.IsSupported || Ssse3.IsSupported)
{
int remainder = Avx2.IsSupported
- ? ImageMaths.ModuloP2(source.Length, Vector256.Count)
- : ImageMaths.ModuloP2(source.Length, Vector128.Count);
+ ? Numerics.ModuloP2(source.Length, Vector256.Count)
+ : Numerics.ModuloP2(source.Length, Vector128.Count);
int adjustedCount = source.Length - remainder;
@@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp
ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
int n = dest.Length / Vector256.Count;
- int m = ImageMaths.Modulo4(n);
+ int m = Numerics.Modulo4(n);
int u = n - m;
for (int i = 0; i < u; i += 4)
@@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp
ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
int n = dest.Length / Vector128.Count;
- int m = ImageMaths.Modulo4(n);
+ int m = Numerics.Modulo4(n);
int u = n - m;
for (int i = 0; i < u; i += 4)
@@ -288,7 +288,7 @@ namespace SixLabors.ImageSharp
ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
int n = dest.Length / Vector256.Count;
- int m = ImageMaths.Modulo4(n);
+ int m = Numerics.Modulo4(n);
int u = n - m;
for (int i = 0; i < u; i += 4)
@@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp
ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
int n = dest.Length / Vector128.Count;
- int m = ImageMaths.Modulo4(n);
+ int m = Numerics.Modulo4(n);
int u = n - m;
for (int i = 0; i < u; i += 4)
@@ -550,11 +550,11 @@ namespace SixLabors.ImageSharp
int remainder;
if (Avx2.IsSupported)
{
- remainder = ImageMaths.ModuloP2(source.Length, Vector256.Count);
+ remainder = Numerics.ModuloP2(source.Length, Vector256.Count);
}
else
{
- remainder = ImageMaths.ModuloP2(source.Length, Vector128.Count);
+ remainder = Numerics.ModuloP2(source.Length, Vector128.Count);
}
int adjustedCount = source.Length - remainder;
@@ -683,11 +683,11 @@ namespace SixLabors.ImageSharp
int remainder;
if (Avx2.IsSupported)
{
- remainder = ImageMaths.ModuloP2(source.Length, Vector256.Count);
+ remainder = Numerics.ModuloP2(source.Length, Vector256.Count);
}
else
{
- remainder = ImageMaths.ModuloP2(source.Length, Vector128.Count);
+ remainder = Numerics.ModuloP2(source.Length, Vector128.Count);
}
int adjustedCount = source.Length - remainder;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs
index 7cbb5bfe35..7691cb9ad0 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs
@@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
DebugGuard.IsTrue(
- ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
+ Numerics.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisible by {shouldBeDivisibleBy}!");
}
@@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
DebugGuard.IsTrue(
- ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
+ Numerics.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisible by {shouldBeDivisibleBy}!");
}
diff --git a/src/ImageSharp/Common/Helpers/Vector4Utilities.cs b/src/ImageSharp/Common/Helpers/Vector4Utilities.cs
index f617e9a3ea..464006570a 100644
--- a/src/ImageSharp/Common/Helpers/Vector4Utilities.cs
+++ b/src/ImageSharp/Common/Helpers/Vector4Utilities.cs
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
- if (ImageMaths.Modulo2(vectors.Length) != 0)
+ if (Numerics.Modulo2(vectors.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
@@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
- if (ImageMaths.Modulo2(vectors.Length) != 0)
+ if (Numerics.Modulo2(vectors.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index 8d9f6e4156..d1c214e3d6 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - (above >> 1));
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - Average(left, above));
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
sum -= 3;
@@ -102,4 +102,4 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Average(byte left, byte above) => (left + above) >> 1;
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 7b5c71a010..fab6788061 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - PaethPredictor(0, above, 0));
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
@@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - PaethPredictor(left, above, upperLeft));
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
sum -= 4;
@@ -111,9 +111,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
private static byte PaethPredictor(byte left, byte above, byte upperLeft)
{
int p = left + above - upperLeft;
- int pa = ImageMaths.FastAbs(p - left);
- int pb = ImageMaths.FastAbs(p - above);
- int pc = ImageMaths.FastAbs(p - upperLeft);
+ int pa = Numerics.Abs(p - left);
+ int pb = Numerics.Abs(p - above);
+ int pc = Numerics.Abs(p - upperLeft);
if (pa <= pb && pa <= pc)
{
@@ -128,4 +128,4 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
return upperLeft;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index c448e71f43..cb4cfb471f 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = scan;
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
@@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - prev);
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
sum -= 1;
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index 2a77bccb97..cf553cbb68 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
++x;
ref byte res = ref Unsafe.Add(ref resultBaseRef, x);
res = (byte)(scan - above);
- sum += ImageMaths.FastAbs(unchecked((sbyte)res));
+ sum += Numerics.Abs(unchecked((sbyte)res));
}
sum -= 2;
diff --git a/src/ImageSharp/Primitives/ComplexVector4.cs b/src/ImageSharp/Primitives/ComplexVector4.cs
index 3a1d4ac460..07f074502f 100644
--- a/src/ImageSharp/Primitives/ComplexVector4.cs
+++ b/src/ImageSharp/Primitives/ComplexVector4.cs
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp
///
/// The input to sum
[MethodImpl(InliningOptions.ShortMethod)]
- public void Sum(in ComplexVector4 value)
+ public void Sum(ComplexVector4 value)
{
this.Real += value.Real;
this.Imaginary += value.Imaginary;
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
index 7b1ceff276..9844f99563 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
- float gx = ImageMaths.Gaussian(x, weight);
+ float gx = Numerics.Gaussian(x, weight);
sum += gx;
kernel[0, i] = gx;
}
@@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
for (int i = 0; i < size; i++)
{
float x = i - midpoint;
- float gx = ImageMaths.Gaussian(x, weight);
+ float gx = Numerics.Gaussian(x, weight);
sum += gx;
kernel[0, i] = gx;
}
@@ -88,4 +88,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
return kernel;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
index a61c68de3b..007a9a05de 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
@@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
if (y < 0)
{
- y = ImageMaths.FastAbs(y);
+ y = Numerics.Abs(y);
}
else if (y >= source.Height)
{
@@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int idx = 0;
for (int dx = x; dx < x + tileWidth; dx++)
{
- rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4();
+ rowPixels[idx] = source[Numerics.Abs(dx), y].ToVector4();
idx++;
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
index 7aefd8f6f1..8742db580a 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float radius = this.Radius;
if (x < radius)
{
- return ImageMaths.SinC(x) * ImageMaths.SinC(x / radius);
+ return Numerics.SinC(x) * Numerics.SinC(x / radius);
}
return 0F;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
index 93c50af132..18859d1ada 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (x < 3F)
{
- return ImageMaths.SinC(x) * (1F - (x * x / 9F));
+ return Numerics.SinC(x) * (1F - (x * x / 9F));
}
return 0F;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
index 7cbda76a54..ab6040c17b 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
@@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// 'ratio' is a rational number.
// Multiplying it by LCM(sourceSize, destSize)/sourceSize will result in a whole number "again".
// This value is determining the length of the periods in repeating kernel map rows.
- int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
+ int period = Numerics.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
// the center position at i == 0:
double center0 = (ratio - 1) * 0.5;
diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs
new file mode 100644
index 0000000000..a5b49721da
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs
@@ -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);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
index 55e26372b0..27ae787bca 100644
--- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
+++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath
[Benchmark]
public int Bitwise()
{
- return ImageMaths.Modulo8(this.value);
+ return Numerics.Modulo8(this.value);
}
}
}
diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
index 9da7b9fdf3..d336015a0a 100644
--- a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
+++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath
[Benchmark]
public int Bitwise()
{
- return ImageMaths.ModuloP2(this.value, this.m);
+ return Numerics.ModuloP2(this.value, this.m);
}
// RESULTS:
diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
index 7d16623877..656d8ce3b2 100644
--- a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs
@@ -10,144 +10,6 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{
public class ImageMathsTests
{
- [Theory]
- [InlineData(0)]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- [InlineData(4)]
- [InlineData(100)]
- [InlineData(123)]
- [InlineData(53436353)]
- public void Modulo2(int x)
- {
- int actual = ImageMaths.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 = ImageMaths.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 = ImageMaths.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 = ImageMaths.ModuloP2(x, m);
- Assert.Equal(x % m, actual);
- }
-
- [Theory]
- [InlineData(0, 0, 0, 0)]
- [InlineData(0.5f, 0, 1, 0.5f)]
- [InlineData(-0.5f, -0.1f, 10, -0.1f)]
- [InlineData(-0.05f, -0.1f, 10, -0.05f)]
- [InlineData(9.9f, -0.1f, 10, 9.9f)]
- [InlineData(10f, -0.1f, 10, 10f)]
- [InlineData(10.1f, -0.1f, 10, 10f)]
- public void Clamp(float x, float min, float max, float expected)
- {
- float actual = x.Clamp(min, max);
- Assert.Equal(expected, actual);
- }
-
- [Fact]
- public void FasAbsResultMatchesMath()
- {
- const int X = -33;
- int expected = Math.Abs(X);
-
- Assert.Equal(expected, ImageMaths.FastAbs(X));
- }
-
- [Fact]
- public void Pow2ResultMatchesMath()
- {
- const float X = -33;
- float expected = (float)Math.Pow(X, 2);
-
- Assert.Equal(expected, ImageMaths.Pow2(X));
- }
-
- [Fact]
- public void Pow3ResultMatchesMath()
- {
- const float X = -33;
- float expected = (float)Math.Pow(X, 3);
-
- Assert.Equal(expected, ImageMaths.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 = ImageMaths.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 = ImageMaths.LeastCommonMultiple(a, b);
-
- Assert.Equal(expected, actual);
- }
-
[Theory]
[InlineData(0.2f, 0.7f, 0.1f, 256, 140)]
[InlineData(0.5f, 0.5f, 0.5f, 256, 128)]
diff --git a/tests/ImageSharp.Tests/Helpers/NumericsTests.cs b/tests/ImageSharp.Tests/Helpers/NumericsTests.cs
new file mode 100644
index 0000000000..f1678cfa1d
--- /dev/null
+++ b/tests/ImageSharp.Tests/Helpers/NumericsTests.cs
@@ -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(Span 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(
+ int length,
+ T min,
+ T max,
+ SpanAction clampAction,
+ Func refClampFunc)
+ where T : unmanaged, IComparable
+ {
+ Span 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 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]);
+ }
+ }
+ }
+}