From 2c3dc41eacdf6ed99bb1227e25b29d6bc027b4e8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 23:44:32 +1100 Subject: [PATCH] Faster Pow functions --- .../Implementation/CieLabToCieXyzConverter.cs | 6 +-- .../Implementation/CieLuvToCieXyzConverter.cs | 2 +- .../HunterLabToCieXyzConverter.cs | 2 +- .../Conversion/Implementation/LCompanding.cs | 2 +- src/ImageSharp/Common/Helpers/ImageMaths.cs | 32 +++++++++------ .../ImageSharp.Benchmarks.csproj | 2 +- .../Colorspaces/ColorSpaceEqualityTests.cs | 3 +- .../Helpers/ImageMathsTests.cs | 39 +++++++++++++++++++ .../TestUtilities/ApproximateFloatComparer.cs | 2 +- 9 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs index d1670c321..d2595db28 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs @@ -24,11 +24,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation float fx = (a / 500F) + fy; float fz = fy - (b / 200F); - float fx3 = MathF.Pow(fx, 3F); - float fz3 = MathF.Pow(fz, 3F); + float fx3 = ImageMaths.Pow3(fx); + float fz3 = ImageMaths.Pow3(fz); float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; - float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? MathF.Pow((l + 16F) / 116F, 3F) : l / CieConstants.Kappa; + float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.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/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieXyzConverter.cs index 179c6fae4..f26aeacec 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieXyzConverter.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation float v0 = ComputeV0(input.WhitePoint); float y = l > CieConstants.Kappa * CieConstants.Epsilon - ? MathF.Pow((l + 16) / 116, 3) + ? ImageMaths.Pow3((l + 16) / 116) : l / CieConstants.Kappa; float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLabToCieXyzConverter.cs index 6531ffd24..9eeffd309 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLabToCieXyzConverter.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation float ka = ComputeKa(input.WhitePoint); float kb = ComputeKb(input.WhitePoint); - float y = MathF.Pow(l / 100F, 2) * yn; + float y = ImageMaths.Pow2(l / 100F) * yn; float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LCompanding.cs index 4af7d96ff..9099d6bed 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LCompanding.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Expand(float channel) { - return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); + return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); } /// diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 75c9190d2..3167a42c8 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -29,6 +29,22 @@ namespace SixLabors.ImageSharp 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; + /// /// Returns how many bits are required to store the specified number of colors. /// Performs a Log2() on the value. @@ -38,10 +54,7 @@ namespace SixLabors.ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetBitsNeededForColorDepth(int colors) - { - return (int)Math.Ceiling(Math.Log(colors, 2)); - } + public static int GetBitsNeededForColorDepth(int colors) => (int)Math.Ceiling(Math.Log(colors, 2)); /// /// Implementation of 1D Gaussian G(x) function @@ -56,7 +69,7 @@ namespace SixLabors.ImageSharp float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; float exponentNumerator = -x * x; - float exponentDenominator = (float)(2 * Math.Pow(sigma, 2)); + float exponentDenominator = 2 * Pow2(sigma); float left = Numerator / denominator; float right = MathF.Exp(exponentNumerator / exponentDenominator); @@ -98,14 +111,12 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetBcValue(float x, float b, float c) { - float temp; - if (x < 0F) { x = -x; } - temp = x * x; + float temp = x * x; if (x < 1F) { x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b)); @@ -134,10 +145,7 @@ namespace SixLabors.ImageSharp /// The bounding . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) - { - return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); - } + public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); /// /// Finds the bounding rectangle based on the first instance of any color component other diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 4e1776816..ceec8f6fb 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs index f0ac56f4f..1d65e7fc7 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.ColorSpaces; using Xunit; // ReSharper disable InconsistentNaming +// TODO: This needs to be refactored so that it uses a serializable type once the colorspace code is public namespace SixLabors.ImageSharp.Tests.Colorspaces { /// @@ -33,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces {nameof( YCbCr), YCbCr.Empty } }; - public static readonly IEnumerable EmptyData = EmptyDataLookup.Select(x => new [] { x.Key }); + public static readonly IEnumerable EmptyData = EmptyDataLookup.Select(x => new[] { x.Key }); public static readonly TheoryData EqualityData = new TheoryData diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs new file mode 100644 index 000000000..c00a0a252 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + using Xunit; + + public class ImageMathsTests + { + [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 = MathF.Pow(X, 2); + + Assert.Equal(expected, ImageMaths.Pow2(X)); + } + + [Fact] + public void Pow3ResultMatchesMath() + { + const float X = -33; + float expected = MathF.Pow(X, 3); + + Assert.Equal(expected, ImageMaths.Pow3(X)); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 24363173a..fd156a7e2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Numerics; using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.Tests {