From ef09d48c1148b4331f8234ab0ef15cf03b4686fc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 18 Feb 2017 12:55:26 +1100 Subject: [PATCH 1/3] 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 a68c2fa44d..6bdd68b9b6 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 272b3cc8b4..8ca1ca6669 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 cdea43e854..71b71ba5d2 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 71b8062617..495bfa5dba 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 aa49401926..46df9c6eea 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 6ee5d0f96a..f86940adef 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 1a88dbe345..58967cb5b1 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 1033111fcb..cd2b91f167 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 f628ea1b94..6e1452e17f 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 3f7e0a00ee..f8cb9aba9d 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 b19c5c7737..4b23dfe470 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 efa6c28c56..304dafbbd1 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 595ca6a4b0..e18957a69a 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 5c48722ef8..1d61b8cd94 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 c64ee8abeb..83f13a3429 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 4e61707c45..a8187caca3 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 de2a185f86..6b9e67ce9e 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 328c903dc7..1607889677 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 7cd3bbe9c5..49733a9bd0 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 d0654dd77b..2254a61b6e 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 fc94c689f0..5720d82186 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); From 680ac0de5af23c72281d259a8abfe97f4c4cc18e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 12:12:04 +0100 Subject: [PATCH 2/3] better Array2D benchmark --- .../ImageSharp.Benchmarks/General/Array2D.cs | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index a01ba77adc..fce92e9be5 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -5,25 +5,31 @@ namespace ImageSharp.Benchmarks.General { + using System; + using BenchmarkDotNet.Attributes; public class Array2D { - private float[,] data; + private float[] flatArray; + + private float[,] array2D; private float[][] jaggedData; private Fast2DArray fastData; - - [Params(10, 100, 1000, 10000)] + + [Params(4, 16, 128)] public int Count { get; set; } - public int Index { get; set; } + public int Min { get; private set; } + public int Max { get; private set; } [Setup] public void SetUp() { - this.data = new float[this.Count, this.Count]; + this.flatArray = new float[this.Count * this.Count]; + this.array2D = new float[this.Count, this.Count]; this.jaggedData = new float[this.Count][]; for (int i = 0; i < this.Count; i++) @@ -31,27 +37,72 @@ namespace ImageSharp.Benchmarks.General this.jaggedData[i] = new float[this.Count]; } - this.fastData = new Fast2DArray(this.data); + this.fastData = new Fast2DArray(this.array2D); - this.Index = this.Count / 2; + this.Min = this.Count / 2 - 10; + this.Min = Math.Max(0, this.Min); + this.Max = this.Min + Math.Min(10, this.Count); + } + + [Benchmark(Description = "Emulated 2D array access using flat array")] + public float FlatArrayIndex() + { + float[] a = this.flatArray; + float s = 0; + int count = this.Count; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[count * i + j]; + } + } + return s; } [Benchmark(Baseline = true, Description = "Array access using 2D array")] - public float ArrayIndex() + public float Array2DIndex() { - return this.data[this.Index, this.Index]; + float s = 0; + float[,] a = this.array2D; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i, j]; + } + } + return s; } [Benchmark(Description = "Array access using a jagged array")] public float ArrayJaggedIndex() { - return this.jaggedData[this.Index][this.Index]; + float s = 0; + float[][] a = this.jaggedData; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i][j]; + } + } + return s; } [Benchmark(Description = "Array access using Fast2DArray")] public float ArrayFastIndex() { - return this.fastData[this.Index, this.Index]; + float s = 0; + Fast2DArray a = this.fastData; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i, j]; + } + } + return s; } } } From 54c65567cea09ecb63382a27ec611ef1e15a54a3 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 18 Feb 2017 11:14:53 +0000 Subject: [PATCH 3/3] Improve Fast2DArray initialisation --- .../Convolution/BoxBlurProcessor.cs | 4 +-- .../EdgeDetection/KayyaliProcessor.cs | 8 ++--- .../EdgeDetection/KirschProcessor.cs | 32 +++++++++---------- .../EdgeDetection/Laplacian3X3Processor.cs | 4 +-- .../EdgeDetection/Laplacian5X5Processor.cs | 4 +-- .../LaplacianOfGaussianProcessor.cs | 4 +-- .../EdgeDetection/PrewittProcessor.cs | 8 ++--- .../EdgeDetection/RobertsCrossProcessor.cs | 8 ++--- .../EdgeDetection/RobinsonProcessor.cs | 32 +++++++++---------- .../EdgeDetection/ScharrProcessor.cs | 8 ++--- .../EdgeDetection/SobelProcessor.cs | 8 ++--- .../Convolution/GaussianBlurProcessor.cs | 4 +-- .../Convolution/GaussianSharpenProcessor.cs | 4 +-- .../Common/Helpers/Fast2DArray{T}.cs | 29 +++++++++++++++++ .../Dithering/ErrorDiffusion/Atkinson.cs | 4 +-- .../Dithering/ErrorDiffusion/Burks.cs | 4 +-- .../ErrorDiffusion/FloydSteinberg.cs | 4 +-- .../ErrorDiffusion/JarvisJudiceNinke.cs | 4 +-- .../Dithering/ErrorDiffusion/Sierra2.cs | 4 +-- .../Dithering/ErrorDiffusion/Sierra3.cs | 4 +-- .../Dithering/ErrorDiffusion/SierraLite.cs | 4 +-- .../Dithering/ErrorDiffusion/Stucki.cs | 4 +-- src/ImageSharp/Dithering/Ordered/Bayer.cs | 4 +-- src/ImageSharp/Dithering/Ordered/Ordered.cs | 4 +-- .../Common/Fast2DArrayTests.cs | 28 +++++++++++++--- 25 files changed, 136 insertions(+), 89 deletions(-) diff --git a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs index 8ca1ca6669..a9caea5559 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -57,8 +57,8 @@ namespace ImageSharp.Processing.Processors { int size = this.kernelSize; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0F; for (int i = 0; i < size; i++) diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index 6e1452e17f..bda3c9317a 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray KayyaliX = - new Fast2DArray(new float[,] + new float[,] { { 6, 0, -6 }, { 0, 0, 0 }, { -6, 0, 6 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray KayyaliY = - new Fast2DArray(new float[,] + new float[,] { { -6, 0, 6 }, { 0, 0, 0 }, { 6, 0, -6 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index f8cb9aba9d..4a555137eb 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -21,89 +21,89 @@ namespace ImageSharp.Processing.Processors /// The North gradient operator /// private static readonly Fast2DArray KirschNorth = - new Fast2DArray(new float[,] + new float[,] { { 5, 5, 5 }, { -3, 0, -3 }, { -3, -3, -3 } - }); + }; /// /// The NorthWest gradient operator /// private static readonly Fast2DArray KirschNorthWest = - new Fast2DArray(new float[,] + new float[,] { { 5, 5, -3 }, { 5, 0, -3 }, { -3, -3, -3 } - }); + }; /// /// The West gradient operator /// private static readonly Fast2DArray KirschWest = - new Fast2DArray(new float[,] + new float[,] { { 5, -3, -3 }, { 5, 0, -3 }, { 5, -3, -3 } - }); + }; /// /// The SouthWest gradient operator /// private static readonly Fast2DArray KirschSouthWest = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { 5, 0, -3 }, { 5, 5, -3 } - }); + }; /// /// The South gradient operator /// private static readonly Fast2DArray KirschSouth = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { -3, 0, -3 }, { 5, 5, 5 } - }); + }; /// /// The SouthEast gradient operator /// private static readonly Fast2DArray KirschSouthEast = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { -3, 0, 5 }, { -3, 5, 5 } - }); + }; /// /// The East gradient operator /// private static readonly Fast2DArray KirschEast = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, 5 }, { -3, 0, 5 }, { -3, -3, 5 } - }); + }; /// /// The NorthEast gradient operator /// private static readonly Fast2DArray KirschNorthEast = - new Fast2DArray(new float[,] + new float[,] { { -3, 5, 5 }, { -3, 0, 5 }, { -3, -3, -3 } - }); + }; /// public override Fast2DArray North => KirschNorth; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index 4b23dfe470..74fcfcad55 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -21,12 +21,12 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray Laplacian3X3XY = - new Fast2DArray(new float[,] + new float[,] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 304dafbbd1..c00831a694 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -21,14 +21,14 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray Laplacian5X5XY = - new Fast2DArray(new float[,] + 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 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index e18957a69a..8ee01047b2 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -21,14 +21,14 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray LaplacianOfGaussianXY = - new Fast2DArray(new float[,] + 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 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 1d61b8cd94..cf213e7240 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray PrewittX = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -1, 0, 1 }, { -1, 0, 1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray PrewittY = - new Fast2DArray(new float[,] + new float[,] { { 1, 1, 1 }, { 0, 0, 0 }, { -1, -1, -1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index 83f13a3429..de1f279ea1 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -21,21 +21,21 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray RobertsCrossX = - new Fast2DArray(new float[,] + new float[,] { { 1, 0 }, { 0, -1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray RobertsCrossY = - new Fast2DArray(new float[,] + new float[,] { { 0, 1 }, { -1, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index a8187caca3..13d7a3f498 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -21,89 +21,89 @@ namespace ImageSharp.Processing.Processors /// The North gradient operator /// private static readonly Fast2DArray RobinsonNorth = - new Fast2DArray(new float[,] + new float[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } - }); + }; /// /// The NorthWest gradient operator /// private static readonly Fast2DArray RobinsonNorthWest = - new Fast2DArray(new float[,] + new float[,] { { 2, 1, 0 }, { 1, 0, -1 }, { 0, -1, -2 } - }); + }; /// /// The West gradient operator /// private static readonly Fast2DArray RobinsonWest = - new Fast2DArray(new float[,] + new float[,] { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } - }); + }; /// /// The SouthWest gradient operator /// private static readonly Fast2DArray RobinsonSouthWest = - new Fast2DArray(new float[,] + new float[,] { { 0, -1, -2 }, { 1, 0, -1 }, { 2, 1, 0 } - }); + }; /// /// The South gradient operator /// private static readonly Fast2DArray RobinsonSouth = - new Fast2DArray(new float[,] + new float[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } - }); + }; /// /// The SouthEast gradient operator /// private static readonly Fast2DArray RobinsonSouthEast = - new Fast2DArray(new float[,] + new float[,] { { -2, -1, 0 }, { -1, 0, 1 }, { 0, 1, 2 } - }); + }; /// /// The East gradient operator /// private static readonly Fast2DArray RobinsonEast = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } - }); + }; /// /// The NorthEast gradient operator /// private static readonly Fast2DArray RobinsonNorthEast = - new Fast2DArray(new float[,] + new float[,] { { 0, 1, 2 }, { -1, 0, 1 }, { -2, -1, 0 } - }); + }; /// public override Fast2DArray North => RobinsonNorth; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index 6b9e67ce9e..11cb91b0d9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray ScharrX = - new Fast2DArray(new float[,] + new float[,] { { -3, 0, 3 }, { -10, 0, 10 }, { -3, 0, 3 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray ScharrY = - new Fast2DArray(new float[,] + new float[,] { { 3, 10, 3 }, { 0, 0, 0 }, { -3, -10, -3 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 1607889677..347a19dab6 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray SobelX = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray SobelY = - new Fast2DArray(new float[,] + new float[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs index 49733a9bd0..87dde5b475 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -94,8 +94,8 @@ namespace ImageSharp.Processing.Processors int size = this.kernelSize; float weight = this.sigma; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0F; float midpoint = (size - 1) / 2F; diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 2254a61b6e..5cf8cdf541 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -96,8 +96,8 @@ namespace ImageSharp.Processing.Processors int size = this.kernelSize; float weight = this.sigma; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0; diff --git a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs index 26ec816ce3..09cb0fec23 100644 --- a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs +++ b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs @@ -30,6 +30,22 @@ namespace ImageSharp /// public int Height; + /// + /// Initializes a new instance of the struct. + /// + /// The width. + /// The height. + public Fast2DArray(int width, int height) + { + this.Height = height; + this.Width = width; + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + this.Data = new T[this.Width * this.Height]; + } + /// /// Initializes a new instance of the struct. /// @@ -77,6 +93,19 @@ namespace ImageSharp } } + /// + /// Performs an implicit conversion from a 2D array to a . + /// + /// The source array. + /// + /// The represenation on the source data. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Fast2DArray(T[,] data) + { + return new Fast2DArray(data); + } + /// /// Checks the coordinates to ensure they are within bounds. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs index 1fa6852c22..b94b872552 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray AtkinsonMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 1, 1 }, { 1, 1, 1, 0 }, { 0, 1, 0, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs index a4adcb0a49..894b6e236e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray BurksMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 8, 4 }, { 2, 4, 8, 4, 2 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs index 7b67d2dd12..f7a93667fe 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray FloydSteinbergMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 7 }, { 3, 5, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs index 32f38fbc85..60fef81216 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray JarvisJudiceNinkeMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 7, 5 }, { 3, 5, 7, 5, 3 }, { 1, 3, 5, 3, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs index 47b14944e2..4325438e08 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray Sierra2Matrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 4, 3 }, { 1, 2, 3, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs index ae33954cfd..25ea70d0a6 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray Sierra3Matrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 5, 3 }, { 2, 4, 5, 4, 2 }, { 0, 2, 3, 2, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs index 8a1e178165..c7b1d214f1 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray SierraLiteMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 2 }, { 1, 1, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs index b5d22b2592..93258c3508 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray StuckiMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 8, 4 }, { 2, 4, 8, 4, 2 }, { 1, 2, 4, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 3d3d900233..ff56196c59 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 /// private static readonly Fast2DArray ThresholdMatrix = - new Fast2DArray(new byte[,] + new byte[,] { { 15, 143, 47, 175 }, { 207, 79, 239, 111 }, { 63, 191, 31, 159 }, { 255, 127, 223, 95 } - }); + }; /// public Fast2DArray Matrix { get; } = ThresholdMatrix; diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index bb1f21060d..86c325cd1f 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered /// This is calculated by multiplying each value in the original matrix by 16 /// private static readonly Fast2DArray ThresholdMatrix = - new Fast2DArray(new byte[,] + new byte[,] { { 0, 128, 32, 160 }, { 192, 64, 224, 96 }, { 48, 176, 16, 144 }, { 240, 112, 208, 80 } - }); + }; /// public Fast2DArray Matrix { get; } = ThresholdMatrix; diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs index 903ea6f8d9..7db7a4820b 100644 --- a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs @@ -21,9 +21,27 @@ namespace ImageSharp.Tests.Common public void Fast2DArrayThrowsOnNullInitializer() { Assert.Throws(() => - { - Fast2DArray fast = new Fast2DArray(null); - }); + { + Fast2DArray fast = new Fast2DArray(null); + }); + } + + [Fact] + public void Fast2DArrayThrowsOnEmptyZeroWidth() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(0, 10); + }); + } + + [Fact] + public void Fast2DArrayThrowsOnEmptyZeroHeight() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(10, 0); + }); } [Fact] @@ -46,7 +64,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Fast2DArrayGetReturnsCorrectResults() { - Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix); + Fast2DArray fast = FloydSteinbergMatrix; for (int row = 0; row < fast.Height; row++) { @@ -60,7 +78,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Fast2DArrayGetSetReturnsCorrectResults() { - Fast2DArray fast = new Fast2DArray(new float[4, 4]); + Fast2DArray fast = new Fast2DArray(4, 4); const float Val = 5F; fast[3, 3] = Val;