diff --git a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs index 323611fbc..ff208f3d7 100644 --- a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs @@ -79,9 +79,9 @@ namespace ImageSharp.Formats private static byte PaethPredicator(byte left, byte above, byte upperLeft) { int p = left + above - upperLeft; - int pa = Math.Abs(p - left); - int pb = Math.Abs(p - above); - int pc = Math.Abs(p - upperLeft); + int pa = ImageMaths.FastAbs(p - left); + int pb = ImageMaths.FastAbs(p - above); + int pc = ImageMaths.FastAbs(p - upperLeft); if (pa <= pb && pa <= pc) { diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 78df6c72e..609556b45 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -369,33 +369,33 @@ namespace ImageSharp.Formats } SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel); - int currentTotalVariation = this.CalculateTotalVariation(this.sub); - int lowestTotalVariation = currentTotalVariation; + int currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); + int lowestSum = currentSum; result = this.sub; UpFilter.Encode(rawScanline, previousScanline, this.up); - currentTotalVariation = this.CalculateTotalVariation(this.up); + currentSum = this.CalculateTotalVariation(this.up, currentSum); - if (currentTotalVariation < lowestTotalVariation) + if (currentSum < lowestSum) { - lowestTotalVariation = currentTotalVariation; + lowestSum = currentSum; result = this.up; } AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel); - currentTotalVariation = this.CalculateTotalVariation(this.average); + currentSum = this.CalculateTotalVariation(this.average, currentSum); - if (currentTotalVariation < lowestTotalVariation) + if (currentSum < lowestSum) { - lowestTotalVariation = currentTotalVariation; + lowestSum = currentSum; result = this.average; } PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel); - currentTotalVariation = this.CalculateTotalVariation(this.paeth); + currentSum = this.CalculateTotalVariation(this.paeth, currentSum); - if (currentTotalVariation < lowestTotalVariation) + if (currentSum < lowestSum) { result = this.paeth; } @@ -408,17 +408,25 @@ namespace ImageSharp.Formats /// neighbor differences. /// /// The scanline bytes + /// The last variation sum /// The - private int CalculateTotalVariation(byte[] scanline) + private int CalculateTotalVariation(byte[] scanline, int lastSum) { - int totalVariation = 0; + int sum = 0; for (int i = 1; i < scanline.Length; i++) { - totalVariation += Math.Abs(scanline[i] - scanline[i - 1]); + byte v = scanline[i]; + sum += v < 128 ? v : 256 - v; + + // No point continuing if we are larger. + if (sum > lastSum) + { + break; + } } - return totalVariation; + return sum; } /// diff --git a/src/ImageSharp/Common/Extensions/ByteExtensions.cs b/src/ImageSharp/Common/Extensions/ByteExtensions.cs index fc9c29e62..a6dc19ded 100644 --- a/src/ImageSharp/Common/Extensions/ByteExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ByteExtensions.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using System.Runtime.CompilerServices; + /// /// Extension methods for the struct. /// @@ -26,6 +28,7 @@ namespace ImageSharp /// The index. /// The length. /// is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReverseBytes(this byte[] source, int index, int length) { Guard.NotNull(source, nameof(source)); diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 7455b542d..147a5914b 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -8,12 +8,26 @@ namespace ImageSharp using System; using System.Linq; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Provides common mathematical methods. /// internal static class ImageMaths { + /// + /// 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(MethodImplOptions.AggressiveInlining)] + public static int FastAbs(int x) + { + return (x ^ (x >> 31)) - (x >> 31); + } + /// /// Returns how many bits are required to store the specified number of colors. /// Performs a Log2() on the value. @@ -273,6 +287,7 @@ namespace ImageSharp /// /// The /// . + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float Clean(float x) { if (Math.Abs(x) < Constants.Epsilon) diff --git a/tests/ImageSharp.Benchmarks/General/Abs.cs b/tests/ImageSharp.Benchmarks/General/Abs.cs new file mode 100644 index 000000000..b0f033b81 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Abs.cs @@ -0,0 +1,33 @@ +namespace ImageSharp.Benchmarks.General +{ + using System; + + using BenchmarkDotNet.Attributes; + + public class Abs + { + [Params(-1, 1)] + public int X { get; set; } + + [Benchmark(Baseline = true, Description = "Maths Abs")] + public int MathAbs() + { + int x = this.X; + return Math.Abs(x); + } + + [Benchmark(Description = "Conditional Abs")] + public int ConditionalAbs() + { + int x = this.X; + return x < 0 ? -x : x; + } + + [Benchmark(Description = "Bitwise Abs")] + public int AbsBitwise() + { + int x = this.X; + return (x ^ (x >> 31)) - (x >> 31); + } + } +}