From ef09d48c1148b4331f8234ab0ef15cf03b4686fc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 18 Feb 2017 12:55:26 +1100 Subject: [PATCH] Use Fast2DArray for all convolution algorithms. --- .../Convolution/BoxBlur.cs | 3 +- .../Convolution/BoxBlurProcessor.cs | 30 ++--- .../Convolution/Convolution2DProcessor.cs | 31 +++-- .../Convolution/Convolution2PassProcessor.cs | 74 +++++------ .../Convolution/ConvolutionProcessor.cs | 29 ++--- .../EdgeDetection/EdgeDetector2DProcessor.cs | 15 ++- .../EdgeDetectorCompassProcessor.cs | 36 +++--- .../EdgeDetection/EdgeDetectorProcessor.cs | 23 +++- .../EdgeDetection/KayyaliProcessor.cs | 38 +++--- .../EdgeDetection/KirschProcessor.cs | 121 ++++++++++-------- .../EdgeDetection/Laplacian3X3Processor.cs | 22 ++-- .../EdgeDetection/Laplacian5X5Processor.cs | 26 ++-- .../LaplacianOfGaussianProcessor.cs | 26 ++-- .../EdgeDetection/PrewittProcessor.cs | 38 +++--- .../EdgeDetection/RobertsCrossProcessor.cs | 34 ++--- .../EdgeDetection/RobinsonProcessor.cs | 121 ++++++++++-------- .../EdgeDetection/ScharrProcessor.cs | 38 +++--- .../EdgeDetection/SobelProcessor.cs | 38 +++--- .../Convolution/GaussianBlurProcessor.cs | 31 ++--- .../Convolution/GaussianSharpenProcessor.cs | 33 +++-- src/ImageSharp/Common/Helpers/ImageMaths.cs | 6 + 21 files changed, 436 insertions(+), 377 deletions(-) diff --git a/src/ImageSharp.Processing/Convolution/BoxBlur.cs b/src/ImageSharp.Processing/Convolution/BoxBlur.cs index a68c2fa44..6bdd68b9b 100644 --- a/src/ImageSharp.Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp.Processing/Convolution/BoxBlur.cs @@ -7,7 +7,6 @@ namespace ImageSharp { using System; - using Processing; using Processing.Processors; /// @@ -41,7 +40,7 @@ namespace ImageSharp public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - source.ApplyProcessor(new BoxBlurProcessor(), rectangle); + source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); return source; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs index 272b3cc8b..8ca1ca666 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -35,12 +35,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -52,46 +52,42 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Box kernel. /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateBoxKernel(bool horizontal) + /// The + private Fast2DArray CreateBoxKernel(bool horizontal) { int size = this.kernelSize; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; - - if (horizontal) - { - kernel[0] = new float[size]; - } - - float sum = 0.0f; + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); + float sum = 0F; for (int i = 0; i < size; i++) { float x = 1; sum += x; if (horizontal) { - kernel[0][i] = x; + kernel[0, i] = x; } else { - kernel[i] = new[] { x }; + kernel[i, 0] = x; } } - // Normalise kernel so that the sum of all weights equals 1 + // Normalize kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index cdea43e85..71b71ba5d 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2DProcessor(float[][] kernelX, float[][] kernelY) + public Convolution2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -30,20 +30,20 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - int kernelYHeight = this.KernelY.Length; - int kernelYWidth = this.KernelY[0].Length; - int kernelXHeight = this.KernelX.Length; - int kernelXWidth = this.KernelX[0].Length; + int kernelYHeight = this.KernelY.Height; + int kernelYWidth = this.KernelY.Width; + int kernelXHeight = this.KernelX.Height; + int kernelXWidth = this.KernelX.Width; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; @@ -89,22 +89,21 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; if (fy < kernelXHeight) { - rX += this.KernelX[fy][fx] * r; - gX += this.KernelX[fy][fx] * g; - bX += this.KernelX[fy][fx] * b; + Vector4 kx = this.KernelX[fy, fx] * currentColor; + rX += kx.X; + gX += kx.Y; + bX += kx.Z; } if (fx < kernelYWidth) { - rY += this.KernelY[fy][fx] * r; - gY += this.KernelY[fy][fx] * g; - bY += this.KernelY[fy][fx] * b; + Vector4 ky = this.KernelY[fy, fx] * currentColor; + rY += ky.X; + gY += ky.Y; + bY += ky.Z; } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index 71b806261..495bfa5db 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -14,14 +14,14 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class Convolution2PassProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2PassProcessor(float[][] kernelX, float[][] kernelY) + public Convolution2PassProcessor(Fast2DArray kernelX, Fast2DArray kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -30,18 +30,16 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][] kernelX = this.KernelX; - float[][] kernelY = this.KernelY; int width = source.Width; int height = source.Height; @@ -50,8 +48,8 @@ namespace ImageSharp.Processing.Processors using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) using (PixelAccessor sourcePixels = source.Lock()) { - this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX); - this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY); + this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX); + this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY); } source.SwapPixelsBuffers(targetPixels); @@ -62,18 +60,16 @@ namespace ImageSharp.Processing.Processors /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// - /// The image width. - /// The image height. /// The target pixels to apply the process to. /// The source pixels. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(int width, int height, PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, float[][] kernel) + private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) { - int kernelHeight = kernel.Length; - int kernelWidth = kernel[0].Length; + int kernelHeight = kernel.Height; + int kernelWidth = kernel.Width; int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; @@ -85,40 +81,40 @@ namespace ImageSharp.Processing.Processors int maxX = endX - 1; Parallel.For( - startY, - endY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) + startY, + endY, + this.ParallelOptions, + y => { - Vector4 destination = default(Vector4); - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radiusY; - int offsetY = y + fyr; + Vector4 destination = default(Vector4); - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelWidth; fx++) + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; + int fyr = fy - radiusY; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); - offsetX = offsetX.Clamp(0, maxX); + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - destination += kernel[fy][fx] * currentColor; + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + destination += kernel[fy, fx] * currentColor; + } } - } - TColor packed = default(TColor); - packed.PackFromVector4(destination); - targetPixels[x, y] = packed; - } - }); + TColor packed = default(TColor); + packed.PackFromVector4(destination); + targetPixels[x, y] = packed; + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs index aa4940192..46df9c6ee 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -14,13 +14,13 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ConvolutionProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The 2d gradient operator. - public ConvolutionProcessor(float[][] kernelXY) + public ConvolutionProcessor(Fast2DArray kernelXY) { this.KernelXY = kernelXY; } @@ -28,13 +28,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the 2d gradient operator. /// - public virtual float[][] KernelXY { get; } + public Fast2DArray KernelXY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][] kernelX = this.KernelXY; - int kernelLength = kernelX.GetLength(0); + int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; int startY = sourceRectangle.Y; @@ -56,9 +55,9 @@ namespace ImageSharp.Processing.Processors { for (int x = startX; x < endX; x++) { - float rX = 0; - float gX = 0; - float bX = 0; + float red = 0; + float green = 0; + float blue = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelLength; fy++) @@ -76,20 +75,14 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; + currentColor *= this.KernelXY[fy, fx]; - rX += kernelX[fy][fx] * r; - gX += kernelX[fy][fx] * g; - bX += kernelX[fy][fx] * b; + red += currentColor.X; + green += currentColor.Y; + blue += currentColor.Z; } } - float red = rX; - float green = gX; - float blue = bX; - TColor packed = default(TColor); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index 6ee5d0f96..f86940ade 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -14,15 +14,26 @@ namespace ImageSharp.Processing.Processors public abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor where TColor : struct, IPackedPixel, IEquatable { + /// + /// Initializes a new instance of the class. + /// + /// The horizontal gradient operator. + /// The vertical gradient operator. + protected EdgeDetector2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) + { + this.KernelX = kernelX; + this.KernelY = kernelY; + } + /// /// Gets the horizontal gradient operator. /// - public abstract float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public abstract float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// public bool Grayscale { get; set; } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 1a88dbe34..58967cb5b 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -19,50 +19,59 @@ namespace ImageSharp.Processing.Processors /// /// Gets the North gradient operator /// - public abstract float[][] North { get; } + public abstract Fast2DArray North { get; } /// /// Gets the NorthWest gradient operator /// - public abstract float[][] NorthWest { get; } + public abstract Fast2DArray NorthWest { get; } /// /// Gets the West gradient operator /// - public abstract float[][] West { get; } + public abstract Fast2DArray West { get; } /// /// Gets the SouthWest gradient operator /// - public abstract float[][] SouthWest { get; } + public abstract Fast2DArray SouthWest { get; } /// /// Gets the South gradient operator /// - public abstract float[][] South { get; } + public abstract Fast2DArray South { get; } /// /// Gets the SouthEast gradient operator /// - public abstract float[][] SouthEast { get; } + public abstract Fast2DArray SouthEast { get; } /// /// Gets the East gradient operator /// - public abstract float[][] East { get; } + public abstract Fast2DArray East { get; } /// /// Gets the NorthEast gradient operator /// - public abstract float[][] NorthEast { get; } + public abstract Fast2DArray NorthEast { get; } /// public bool Grayscale { get; set; } + /// + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + { + if (this.Grayscale) + { + new GrayscaleBt709Processor().Apply(source, sourceRectangle); + } + } + /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][][] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; + Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -132,14 +141,5 @@ namespace ImageSharp.Processing.Processors } } } - - /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) - { - if (this.Grayscale) - { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); - } - } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index 1033111fc..cd2b91f16 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -14,19 +14,22 @@ namespace ImageSharp.Processing.Processors public abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor where TColor : struct, IPackedPixel, IEquatable { + /// + /// Initializes a new instance of the class. + /// + /// The 2d gradient operator. + protected EdgeDetectorProcessor(Fast2DArray kernelXY) + { + this.KernelXY = kernelXY; + } + /// public bool Grayscale { get; set; } /// /// Gets the 2d gradient operator. /// - public abstract float[][] KernelXY { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); - } + public Fast2DArray KernelXY { get; } /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) @@ -36,5 +39,11 @@ namespace ImageSharp.Processing.Processors new GrayscaleBt709Processor().Apply(source, sourceRectangle); } } + + /// + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + { + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index f628ea1b9..6e1452e17 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] KayyaliX = - { - new float[] { 6, 0, -6 }, - new float[] { 0, 0, 0 }, - new float[] { -6, 0, 6 } - }; + private static readonly Fast2DArray KayyaliX = + new Fast2DArray(new float[,] + { + { 6, 0, -6 }, + { 0, 0, 0 }, + { -6, 0, 6 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] KayyaliY = - { - new float[] { -6, 0, 6 }, - new float[] { 0, 0, 0 }, - new float[] { 6, 0, -6 } - }; - - /// - public override float[][] KernelX => KayyaliX; + private static readonly Fast2DArray KayyaliY = + new Fast2DArray(new float[,] + { + { -6, 0, 6 }, + { 0, 0, 0 }, + { 6, 0, -6 } + }); - /// - public override float[][] KernelY => KayyaliY; + /// + /// Initializes a new instance of the class. + /// + public KayyaliProcessor() + : base(KayyaliX, KayyaliY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index 3f7e0a00e..f8cb9aba9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Processing.Processors { using System; @@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors /// /// The North gradient operator /// - private static readonly float[][] KirschNorth = - { - new float[] { 5, 5, 5 }, - new float[] { -3, 0, -3 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorth = + new Fast2DArray(new float[,] + { + { 5, 5, 5 }, + { -3, 0, -3 }, + { -3, -3, -3 } + }); /// /// The NorthWest gradient operator /// - private static readonly float[][] KirschNorthWest = - { - new float[] { 5, 5, -3 }, - new float[] { 5, 0, -3 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorthWest = + new Fast2DArray(new float[,] + { + { 5, 5, -3 }, + { 5, 0, -3 }, + { -3, -3, -3 } + }); /// /// The West gradient operator /// - private static readonly float[][] KirschWest = - { - new float[] { 5, -3, -3 }, - new float[] { 5, 0, -3 }, - new float[] { 5, -3, -3 } - }; + private static readonly Fast2DArray KirschWest = + new Fast2DArray(new float[,] + { + { 5, -3, -3 }, + { 5, 0, -3 }, + { 5, -3, -3 } + }); /// /// The SouthWest gradient operator /// - private static readonly float[][] KirschSouthWest = - { - new float[] { -3, -3, -3 }, - new float[] { 5, 0, -3 }, - new float[] { 5, 5, -3 } - }; + private static readonly Fast2DArray KirschSouthWest = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { 5, 0, -3 }, + { 5, 5, -3 } + }); /// /// The South gradient operator /// - private static readonly float[][] KirschSouth = - { - new float[] { -3, -3, -3 }, - new float[] { -3, 0, -3 }, - new float[] { 5, 5, 5 } - }; + private static readonly Fast2DArray KirschSouth = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { -3, 0, -3 }, + { 5, 5, 5 } + }); /// /// The SouthEast gradient operator /// - private static readonly float[][] KirschSouthEast = - { - new float[] { -3, -3, -3 }, - new float[] { -3, 0, 5 }, - new float[] { -3, 5, 5 } - }; + private static readonly Fast2DArray KirschSouthEast = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { -3, 0, 5 }, + { -3, 5, 5 } + }); /// /// The East gradient operator /// - private static readonly float[][] KirschEast = - { - new float[] { -3, -3, 5 }, - new float[] { -3, 0, 5 }, - new float[] { -3, -3, 5 } - }; + private static readonly Fast2DArray KirschEast = + new Fast2DArray(new float[,] + { + { -3, -3, 5 }, + { -3, 0, 5 }, + { -3, -3, 5 } + }); /// /// The NorthEast gradient operator /// - private static readonly float[][] KirschNorthEast = - { - new float[] { -3, 5, 5 }, - new float[] { -3, 0, 5 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorthEast = + new Fast2DArray(new float[,] + { + { -3, 5, 5 }, + { -3, 0, 5 }, + { -3, -3, -3 } + }); /// - public override float[][] North => KirschNorth; + public override Fast2DArray North => KirschNorth; /// - public override float[][] NorthWest => KirschNorthWest; + public override Fast2DArray NorthWest => KirschNorthWest; /// - public override float[][] West => KirschWest; + public override Fast2DArray West => KirschWest; /// - public override float[][] SouthWest => KirschSouthWest; + public override Fast2DArray SouthWest => KirschSouthWest; /// - public override float[][] South => KirschSouth; + public override Fast2DArray South => KirschSouth; /// - public override float[][] SouthEast => KirschSouthEast; + public override Fast2DArray SouthEast => KirschSouthEast; /// - public override float[][] East => KirschEast; + public override Fast2DArray East => KirschEast; /// - public override float[][] NorthEast => KirschNorthEast; + public override Fast2DArray NorthEast => KirschNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index b19c5c773..4b23dfe47 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -20,14 +20,20 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] Laplacian3X3XY = new float[][] - { - new float[] { -1, -1, -1 }, - new float[] { -1, 8, -1 }, - new float[] { -1, -1, -1 } - }; + private static readonly Fast2DArray Laplacian3X3XY = + new Fast2DArray(new float[,] + { + { -1, -1, -1 }, + { -1, 8, -1 }, + { -1, -1, -1 } + }); - /// - public override float[][] KernelXY => Laplacian3X3XY; + /// + /// Initializes a new instance of the class. + /// + public Laplacian3X3Processor() + : base(Laplacian3X3XY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index efa6c28c5..304dafbbd 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] Laplacian5X5XY = - { - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, 24, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 } - }; + private static readonly Fast2DArray Laplacian5X5XY = + new Fast2DArray(new float[,] + { + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, 24, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 } + }); - /// - public override float[][] KernelXY => Laplacian5X5XY; + /// + /// Initializes a new instance of the class. + /// + public Laplacian5X5Processor() + : base(Laplacian5X5XY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index 595ca6a4b..e18957a69 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] LaplacianOfGaussianXY = - { - new float[] { 0, 0, -1, 0, 0 }, - new float[] { 0, -1, -2, -1, 0 }, - new float[] { -1, -2, 16, -2, -1 }, - new float[] { 0, -1, -2, -1, 0 }, - new float[] { 0, 0, -1, 0, 0 } - }; + private static readonly Fast2DArray LaplacianOfGaussianXY = + new Fast2DArray(new float[,] + { + { 0, 0, -1, 0, 0 }, + { 0, -1, -2, -1, 0 }, + { -1, -2, 16, -2, -1 }, + { 0, -1, -2, -1, 0 }, + { 0, 0, -1, 0, 0 } + }); - /// - public override float[][] KernelXY => LaplacianOfGaussianXY; + /// + /// Initializes a new instance of the class. + /// + public LaplacianOfGaussianProcessor() + : base(LaplacianOfGaussianXY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 5c48722ef..1d61b8cd9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] PrewittX = - { - new float[] { -1, 0, 1 }, - new float[] { -1, 0, 1 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray PrewittX = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -1, 0, 1 }, + { -1, 0, 1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] PrewittY = - { - new float[] { 1, 1, 1 }, - new float[] { 0, 0, 0 }, - new float[] { -1, -1, -1 } - }; - - /// - public override float[][] KernelX => PrewittX; + private static readonly Fast2DArray PrewittY = + new Fast2DArray(new float[,] + { + { 1, 1, 1 }, + { 0, 0, 0 }, + { -1, -1, -1 } + }); - /// - public override float[][] KernelY => PrewittY; + /// + /// Initializes a new instance of the class. + /// + public PrewittProcessor() + : base(PrewittX, PrewittY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index c64ee8abe..83f13a342 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -20,25 +20,29 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] RobertsCrossX = - { - new float[] { 1, 0 }, - new float[] { 0, -1 } - }; + private static readonly Fast2DArray RobertsCrossX = + new Fast2DArray(new float[,] + { + { 1, 0 }, + { 0, -1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] RobertsCrossY = - { - new float[] { 0, 1 }, - new float[] { -1, 0 } - }; - - /// - public override float[][] KernelX => RobertsCrossX; + private static readonly Fast2DArray RobertsCrossY = + new Fast2DArray(new float[,] + { + { 0, 1 }, + { -1, 0 } + }); - /// - public override float[][] KernelY => RobertsCrossY; + /// + /// Initializes a new instance of the class. + /// + public RobertsCrossProcessor() + : base(RobertsCrossX, RobertsCrossY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index 4e61707c4..a8187caca 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Processing.Processors { using System; @@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors /// /// The North gradient operator /// - private static readonly float[][] RobinsonNorth = - { - new float[] { 1, 2, 1 }, - new float[] { 0, 0, 0 }, - new float[] { -1, -2, -1 } - }; + private static readonly Fast2DArray RobinsonNorth = + new Fast2DArray(new float[,] + { + { 1, 2, 1 }, + { 0, 0, 0 }, + { -1, -2, -1 } + }); /// /// The NorthWest gradient operator /// - private static readonly float[][] RobinsonNorthWest = - { - new float[] { 2, 1, 0 }, - new float[] { 1, 0, -1 }, - new float[] { 0, -1, -2 } - }; + private static readonly Fast2DArray RobinsonNorthWest = + new Fast2DArray(new float[,] + { + { 2, 1, 0 }, + { 1, 0, -1 }, + { 0, -1, -2 } + }); /// /// The West gradient operator /// - private static readonly float[][] RobinsonWest = - { - new float[] { 1, 0, -1 }, - new float[] { 2, 0, -2 }, - new float[] { 1, 0, -1 } - }; + private static readonly Fast2DArray RobinsonWest = + new Fast2DArray(new float[,] + { + { 1, 0, -1 }, + { 2, 0, -2 }, + { 1, 0, -1 } + }); /// /// The SouthWest gradient operator /// - private static readonly float[][] RobinsonSouthWest = - { - new float[] { 0, -1, -2 }, - new float[] { 1, 0, -1 }, - new float[] { 2, 1, 0 } - }; + private static readonly Fast2DArray RobinsonSouthWest = + new Fast2DArray(new float[,] + { + { 0, -1, -2 }, + { 1, 0, -1 }, + { 2, 1, 0 } + }); /// /// The South gradient operator /// - private static readonly float[][] RobinsonSouth = - { - new float[] { -1, -2, -1 }, - new float[] { 0, 0, 0 }, - new float[] { 1, 2, 1 } - }; + private static readonly Fast2DArray RobinsonSouth = + new Fast2DArray(new float[,] + { + { -1, -2, -1 }, + { 0, 0, 0 }, + { 1, 2, 1 } + }); /// /// The SouthEast gradient operator /// - private static readonly float[][] RobinsonSouthEast = - { - new float[] { -2, -1, 0 }, - new float[] { -1, 0, 1 }, - new float[] { 0, 1, 2 } - }; + private static readonly Fast2DArray RobinsonSouthEast = + new Fast2DArray(new float[,] + { + { -2, -1, 0 }, + { -1, 0, 1 }, + { 0, 1, 2 } + }); /// /// The East gradient operator /// - private static readonly float[][] RobinsonEast = - { - new float[] { -1, 0, 1 }, - new float[] { -2, 0, 2 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray RobinsonEast = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } + }); /// /// The NorthEast gradient operator /// - private static readonly float[][] RobinsonNorthEast = - { - new float[] { 0, 1, 2 }, - new float[] { -1, 0, 1 }, - new float[] { -2, -1, 0 } - }; + private static readonly Fast2DArray RobinsonNorthEast = + new Fast2DArray(new float[,] + { + { 0, 1, 2 }, + { -1, 0, 1 }, + { -2, -1, 0 } + }); /// - public override float[][] North => RobinsonNorth; + public override Fast2DArray North => RobinsonNorth; /// - public override float[][] NorthWest => RobinsonNorthWest; + public override Fast2DArray NorthWest => RobinsonNorthWest; /// - public override float[][] West => RobinsonWest; + public override Fast2DArray West => RobinsonWest; /// - public override float[][] SouthWest => RobinsonSouthWest; + public override Fast2DArray SouthWest => RobinsonSouthWest; /// - public override float[][] South => RobinsonSouth; + public override Fast2DArray South => RobinsonSouth; /// - public override float[][] SouthEast => RobinsonSouthEast; + public override Fast2DArray SouthEast => RobinsonSouthEast; /// - public override float[][] East => RobinsonEast; + public override Fast2DArray East => RobinsonEast; /// - public override float[][] NorthEast => RobinsonNorthEast; + public override Fast2DArray NorthEast => RobinsonNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index de2a185f8..6b9e67ce9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] ScharrX = new float[3][] - { - new float[] { -3, 0, 3 }, - new float[] { -10, 0, 10 }, - new float[] { -3, 0, 3 } - }; + private static readonly Fast2DArray ScharrX = + new Fast2DArray(new float[,] + { + { -3, 0, 3 }, + { -10, 0, 10 }, + { -3, 0, 3 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] ScharrY = new float[3][] - { - new float[] { 3, 10, 3 }, - new float[] { 0, 0, 0 }, - new float[] { -3, -10, -3 } - }; - - /// - public override float[][] KernelX => ScharrX; + private static readonly Fast2DArray ScharrY = + new Fast2DArray(new float[,] + { + { 3, 10, 3 }, + { 0, 0, 0 }, + { -3, -10, -3 } + }); - /// - public override float[][] KernelY => ScharrY; + /// + /// Initializes a new instance of the class. + /// + public ScharrProcessor() + : base(ScharrX, ScharrY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 328c903dc..160788967 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] SobelX = - { - new float[] { -1, 0, 1 }, - new float[] { -2, 0, 2 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray SobelX = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] SobelY = - { - new float[] { -1, -2, -1 }, - new float[] { 0, 0, 0 }, - new float[] { 1, 2, 1 } - }; - - /// - public override float[][] KernelX => SobelX; + private static readonly Fast2DArray SobelY = + new Fast2DArray(new float[,] + { + { -1, -2, -1 }, + { 0, 0, 0 }, + { 1, 2, 1 } + }); - /// - public override float[][] KernelY => SobelY; + /// + /// Initializes a new instance of the class. + /// + public SobelProcessor() + : base(SobelX, SobelY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs index 7cd3bbe9c..49733a9bd 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -71,12 +71,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -88,21 +88,18 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateGaussianKernel(bool horizontal) + /// The + private Fast2DArray CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); - if (horizontal) - { - kernel[0] = new float[size]; - } - - float sum = 0.0f; + float sum = 0F; + float midpoint = (size - 1) / 2F; - float midpoint = (size - 1) / 2f; for (int i = 0; i < size; i++) { float x = i - midpoint; @@ -110,27 +107,27 @@ namespace ImageSharp.Processing.Processors sum += gx; if (horizontal) { - kernel[0][i] = gx; + kernel[0, i] = gx; } else { - kernel[i] = new[] { gx }; + kernel[i, 0] = gx; } } - // Normalise kernel so that the sum of all weights equals 1 + // Normalize kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs index d0654dd77..2254a61b6 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -73,12 +73,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -90,17 +90,14 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateGaussianKernel(bool horizontal) + /// The + private Fast2DArray CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; - - if (horizontal) - { - kernel[0] = new float[size]; - } + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); float sum = 0; @@ -112,11 +109,11 @@ namespace ImageSharp.Processing.Processors sum += gx; if (horizontal) { - kernel[0][i] = gx; + kernel[0, i] = gx; } else { - kernel[i] = new[] { gx }; + kernel[i, 0] = gx; } } @@ -130,12 +127,12 @@ namespace ImageSharp.Processing.Processors if (i == midpointRounded) { // Calculate central value - kernel[0][i] = (2f * sum) - kernel[0][i]; + kernel[0, i] = (2F * sum) - kernel[0, i]; } else { // invert value - kernel[0][i] = -kernel[0][i]; + kernel[0, i] = -kernel[0, i]; } } } @@ -146,12 +143,12 @@ namespace ImageSharp.Processing.Processors if (i == midpointRounded) { // Calculate central value - kernel[i][0] = (2 * sum) - kernel[i][0]; + kernel[i, 0] = (2 * sum) - kernel[i, 0]; } else { // invert value - kernel[i][0] = -kernel[i][0]; + kernel[i, 0] = -kernel[i, 0]; } } } @@ -161,14 +158,14 @@ namespace ImageSharp.Processing.Processors { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index fc94c689f..5720d8218 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -37,6 +37,7 @@ namespace ImageSharp /// /// The /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBitsNeededForColorDepth(int colors) { return (int)Math.Ceiling(Math.Log(colors, 2)); @@ -48,6 +49,7 @@ namespace ImageSharp /// 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; @@ -72,6 +74,7 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetBcValue(float x, float b, float c) { float temp; @@ -104,6 +107,7 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float SinC(float x) { if (Math.Abs(x) > Constants.Epsilon) @@ -122,6 +126,7 @@ namespace ImageSharp /// /// The representing the degree as radians. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float DegreesToRadians(float degrees) { return degrees * (float)(Math.PI / 180); @@ -139,6 +144,7 @@ namespace 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);