From 22ee2bb1a476841df8481a9ace3ec54a4ef81573 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 8 Mar 2018 00:21:30 +1100 Subject: [PATCH] Replace Fast2DArray with DenseMatrix Much better and faster (comprable to 1D array) --- .../Brushes/PatternBrush{TPixel}.cs | 28 +-- .../ErrorDiffusion/AtkinsonDiffuser.cs | 3 +- .../Dithering/ErrorDiffusion/BurksDiffuser.cs | 4 +- .../ErrorDiffusion/ErrorDiffuserBase.cs | 10 +- .../ErrorDiffusion/FloydSteinbergDiffuser.cs | 4 +- .../JarvisJudiceNinkeDiffuser.cs | 4 +- .../ErrorDiffusion/Sierra2Diffuser.cs | 4 +- .../ErrorDiffusion/Sierra3Diffuser.cs | 4 +- .../ErrorDiffusion/SierraLiteDiffuser.cs | 4 +- .../ErrorDiffusion/StevensonArceDiffuser.cs | 4 +- .../ErrorDiffusion/StuckiDiffuser.cs | 4 +- .../Dithering/Ordered/OrderedDither.cs | 14 +- .../Dithering/Ordered/OrderedDitherFactory.cs | 12 +- src/ImageSharp/Memory/Fast2DArray{T}.cs | 173 -------------- .../ICC/DataWriter/IccDataWriter.Matrix.cs | 8 +- .../IccMatrixProcessElement.cs | 18 +- src/ImageSharp/Primitives/DenseMatrix{T}.cs | 215 ++++++++++++++++++ .../{Numerics => Primitives}/ValueSize.cs | 16 +- .../Processors/BoxBlurProcessor.cs | 17 +- .../Processors/Convolution2DProcessor.cs | 17 +- .../Processors/Convolution2PassProcessor.cs | 16 +- .../Processors/ConvolutionProcessor.cs | 9 +- .../Processors/EdgeDetector2DProcessor.cs | 9 +- .../EdgeDetectorCompassProcessor.cs | 21 +- .../Processors/EdgeDetectorProcessor.cs | 7 +- .../Processors/GaussianBlurProcessor.cs | 17 +- .../Processors/GaussianSharpenProcessor.cs | 17 +- .../Convolution/Processors/KayyaliKernels.cs | 6 +- .../Convolution/Processors/KirschProcessor.cs | 18 +- .../Convolution/Processors/KirshKernels.cs | 18 +- .../Processors/LaplacianKernelFactory.cs | 17 +- .../Processors/LaplacianKernels.cs | 8 +- .../Convolution/Processors/PrewittKernels.cs | 6 +- .../Processors/RobertsCrossKernels.cs | 6 +- .../Convolution/Processors/RobinsonKernels.cs | 18 +- .../Processors/RobinsonProcessor.cs | 18 +- .../Convolution/Processors/ScharrKernels.cs | 6 +- .../Convolution/Processors/SobelKernels.cs | 6 +- src/ImageSharp/Processing/Overlays/Glow.cs | 1 + .../Processing/Overlays/Vignette.cs | 1 + .../Processors/Overlays/GlowProcessor.cs | 1 + .../Processors/Overlays/VignetteProcessor.cs | 1 + .../ImageSharp.Benchmarks/General/Array2D.cs | 47 ++-- .../Drawing/SolidBezierTests.cs | 4 +- .../Primitives/DenseMatrixTests.cs | 108 +++++++++ .../Processing/Overlays/GlowTest.cs | 1 + .../Processing/Overlays/VignetteTest.cs | 1 + 47 files changed, 559 insertions(+), 392 deletions(-) delete mode 100644 src/ImageSharp/Memory/Fast2DArray{T}.cs create mode 100644 src/ImageSharp/Primitives/DenseMatrix{T}.cs rename src/ImageSharp/{Numerics => Primitives}/ValueSize.cs (90%) create mode 100644 tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index cc22b2639..ba6d91208 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -5,9 +5,9 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Drawing.Brushes.Processors; -using SixLabors.ImageSharp.Drawing.Processors; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Drawing.Brushes @@ -40,8 +40,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// The pattern. /// - private readonly Fast2DArray pattern; - private readonly Fast2DArray patternVector; + private readonly DenseMatrix pattern; + private readonly DenseMatrix patternVector; /// /// Initializes a new instance of the class. @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// Color of the back. /// The pattern. public PatternBrush(TPixel foreColor, TPixel backColor, bool[,] pattern) - : this(foreColor, backColor, new Fast2DArray(pattern)) + : this(foreColor, backColor, new DenseMatrix(pattern)) { } @@ -60,12 +60,12 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// Color of the fore. /// Color of the back. /// The pattern. - internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray pattern) + internal PatternBrush(TPixel foreColor, TPixel backColor, DenseMatrix pattern) { var foreColorVector = foreColor.ToVector4(); var backColorVector = backColor.ToVector4(); - this.pattern = new Fast2DArray(pattern.Width, pattern.Height); - this.patternVector = new Fast2DArray(pattern.Width, pattern.Height); + this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); + this.patternVector = new DenseMatrix(pattern.Columns, pattern.Rows); for (int i = 0; i < pattern.Data.Length; i++) { if (pattern.Data[i]) @@ -105,8 +105,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// The pattern. /// - private readonly Fast2DArray pattern; - private readonly Fast2DArray patternVector; + private readonly DenseMatrix pattern; + private readonly DenseMatrix patternVector; /// /// Initializes a new instance of the class. @@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageFrame source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; @@ -134,8 +134,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { get { - x = x % this.pattern.Width; - y = y % this.pattern.Height; + x = x % this.pattern.Columns; + y = y % this.pattern.Rows; // 2d array index at row/column return this.pattern[y, x]; @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - int patternY = y % this.pattern.Height; + int patternY = y % this.pattern.Rows; MemoryManager memoryManager = this.Target.MemoryManager; using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); - int patternX = (x + i) % this.pattern.Width; + int patternX = (x + i) % this.pattern.Columns; overlaySpan[i] = this.pattern[patternY, patternX]; } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs index 3899b14cc..f5219b216 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Dithering.Base; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray AtkinsonMatrix = + private static readonly DenseMatrix AtkinsonMatrix = new float[,] { { 0, 0, 1, 1 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs index 4d9f4d3c4..9505417e5 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray BurksMatrix = + private static readonly DenseMatrix BurksMatrix = new float[,] { { 0, 0, 0, 8, 4 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 8f448198b..ddfac86a1 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -5,8 +5,8 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering.Base { @@ -38,21 +38,21 @@ namespace SixLabors.ImageSharp.Dithering.Base /// /// The diffusion matrix /// - private readonly Fast2DArray matrix; + private readonly DenseMatrix matrix; /// /// Initializes a new instance of the class. /// /// The dithering matrix. /// The divisor. - internal ErrorDiffuserBase(Fast2DArray matrix, byte divisor) + internal ErrorDiffuserBase(DenseMatrix matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); this.matrix = matrix; - this.matrixWidth = this.matrix.Width; - this.matrixHeight = this.matrix.Height; + this.matrixWidth = this.matrix.Columns; + this.matrixHeight = this.matrix.Rows; this.divisorVector = new Vector4(divisor); this.startingOffset = 0; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs index 6457fbe01..dd06ed199 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray FloydSteinbergMatrix = + private static readonly DenseMatrix FloydSteinbergMatrix = new float[,] { { 0, 0, 7 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs index 30e09b47a..48d2f3a97 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray JarvisJudiceNinkeMatrix = + private static readonly DenseMatrix JarvisJudiceNinkeMatrix = new float[,] { { 0, 0, 0, 7, 5 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs index c472d25b0..3aa37798d 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray Sierra2Matrix = + private static readonly DenseMatrix Sierra2Matrix = new float[,] { { 0, 0, 0, 4, 3 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs index c19ab2aaa..e6875b1d4 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray Sierra3Matrix = + private static readonly DenseMatrix Sierra3Matrix = new float[,] { { 0, 0, 0, 5, 3 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs index 263bae568..371f39fe5 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray SierraLiteMatrix = + private static readonly DenseMatrix SierraLiteMatrix = new float[,] { { 0, 0, 2 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs index 0f0338ac7..beda8efbf 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray StevensonArceMatrix = + private static readonly DenseMatrix StevensonArceMatrix = new float[,] { { 0, 0, 0, 0, 0, 32, 0 }, diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs index 071769506..54c448c28 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly Fast2DArray StuckiMatrix = + private static readonly DenseMatrix StuckiMatrix = new float[,] { { 0, 0, 0, 8, 4 }, diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs index c07b185bb..6fa90545a 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Dithering /// public class OrderedDither : IOrderedDither { - private readonly Fast2DArray thresholdMatrix; + private readonly DenseMatrix thresholdMatrix; private readonly int modulusX; private readonly int modulusY; @@ -21,17 +21,17 @@ namespace SixLabors.ImageSharp.Dithering /// The length of the matrix sides public OrderedDither(uint length) { - Fast2DArray ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); - this.modulusX = ditherMatrix.Width; - this.modulusY = ditherMatrix.Height; + DenseMatrix ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); + this.modulusX = ditherMatrix.Columns; + this.modulusY = ditherMatrix.Rows; // Adjust the matrix range for 0-255 // It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2 // https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg int multiplier = 256 / ditherMatrix.Count; - for (int y = 0; y < ditherMatrix.Height; y++) + for (int y = 0; y < ditherMatrix.Rows; y++) { - for (int x = 0; x < ditherMatrix.Width; x++) + for (int x = 0; x < ditherMatrix.Columns; x++) { ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1; } diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs index fc9ac2551..78bc5fff9 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Dithering { @@ -16,8 +16,8 @@ namespace SixLabors.ImageSharp.Dithering /// /// /// The length of the matrix sides - /// The - public static Fast2DArray CreateDitherMatrix(uint length) + /// The + public static DenseMatrix CreateDitherMatrix(uint length) { // Calculate the the logarithm of length to the base 2 uint exponent = 0; @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Dithering while (length > bayerLength); // Create our Bayer matrix that matches the given exponent and dimensions - var matrix = new Fast2DArray((int)length); + var matrix = new DenseMatrix((int)length); uint i = 0; for (int y = 0; y < length; y++) { @@ -81,9 +81,9 @@ namespace SixLabors.ImageSharp.Dithering uint result = 0; for (uint i = 0; i < order; ++i) { - uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1); + uint xOddXorYOdd = (x & 1) ^ (y & 1); uint xOdd = x & 1; - result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd; + result = ((result << 1 | xOddXorYOdd) << 1) | xOdd; x >>= 1; y >>= 1; } diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs deleted file mode 100644 index 38ccdd279..000000000 --- a/src/ImageSharp/Memory/Fast2DArray{T}.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Provides fast access to 2D arrays. - /// - /// The type of elements in the array. - internal struct Fast2DArray - { - /// - /// The 1D representation of the 2D array. - /// - public T[] Data; - - /// - /// Gets the width of the 2D array. - /// - public int Width; - - /// - /// Gets the height of the 2D array. - /// - public int Height; - - /// - /// Gets the number of items in the 2D array - /// - public int Count; - - /// - /// Initializes a new instance of the struct. - /// - /// The length of each dimension. - public Fast2DArray(int length) - : this(length, length) - { - } - - /// - /// 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.Count = width * height; - this.Data = new T[this.Count]; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The 2D array to provide access to. - public Fast2DArray(T[,] data) - { - Guard.NotNull(data, nameof(data)); - this.Height = data.GetLength(0); - this.Width = data.GetLength(1); - - Guard.MustBeGreaterThan(this.Width, 0, nameof(this.Width)); - Guard.MustBeGreaterThan(this.Height, 0, nameof(this.Height)); - - this.Count = this.Width * this.Height; - this.Data = new T[this.Count]; - - for (int y = 0; y < this.Height; y++) - { - for (int x = 0; x < this.Width; x++) - { - this.Data[(y * this.Width) + x] = data[y, x]; - } - } - } - - /// - /// Gets or sets the item at the specified position. - /// - /// The row-coordinate of the item. Must be greater than or equal to zero and less than the height of the array. - /// The column-coordinate of the item. Must be greater than or equal to zero and less than the width of the array. - /// The at the specified position. - public T this[int row, int column] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - this.CheckCoordinates(row, column); - return this.Data[(row * this.Width) + column]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.CheckCoordinates(row, column); - this.Data[(row * this.Width) + column] = value; - } - } - - /// - /// Performs an implicit conversion from a 2D array to a . - /// - /// The source array. - /// - /// The representation on the source data. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Fast2DArray(T[,] data) - { - return new Fast2DArray(data); - } - - /// - /// Gets a representing the row beginning from the the first item on that row. - /// - /// The y-coordinate of the row. Must be greater than or equal to zero and less than the height of the 2D array. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int row) - { - this.CheckCoordinates(row); - return new Span(this.Data, row * this.Width, this.Width); - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. - /// The x-coordinate of the item. Must be greater than zero and smaller than the width of the array. - /// - /// Thrown if the coordinates are not within the bounds of the array. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int row, int column) - { - if (row < 0 || row >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); - } - - if (column < 0 || column >= this.Width) - { - throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds."); - } - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int row) - { - if (row < 0 || row >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index 0b7064cf9..5d7d729b2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { @@ -59,12 +59,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// The matrix to write /// True if the values are encoded as Single; false if encoded as Fix16 /// The number of bytes written - public int WriteMatrix(Fast2DArray value, bool isSingle) + public int WriteMatrix(DenseMatrix value, bool isSingle) { int count = 0; - for (int y = 0; y < value.Height; y++) + for (int y = 0; y < value.Rows; y++) { - for (int x = 0; x < value.Width; x++) + for (int x = 0; x < value.Columns; x++) { if (isSingle) { diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs index 642f766d5..13b58161c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs @@ -3,7 +3,8 @@ using System; using System.Linq; -using SixLabors.ImageSharp.Memory; + +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { @@ -33,7 +34,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Gets the two dimensional matrix with size of Input-Channels x Output-Channels /// - public Fast2DArray MatrixIxO { get; } + public DenseMatrix MatrixIxO { get; } /// /// Gets the one dimensional matrix with size of Output-Channels x 1 @@ -60,18 +61,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc private bool EqualsMatrix(IccMatrixProcessElement element) { - for (int x = 0; x < this.MatrixIxO.Width; x++) - { - for (int y = 0; y < this.MatrixIxO.Height; y++) - { - if (this.MatrixIxO[x, y] != element.MatrixIxO[x, y]) - { - return false; - } - } - } - - return true; + return this.MatrixIxO.Equals(element.MatrixIxO); } } } diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs new file mode 100644 index 000000000..1f459e7cb --- /dev/null +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -0,0 +1,215 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Primitives +{ + /// + /// Represents a dense matrix with arbitrary elements. + /// Components that are adjacent in a column of the matrix are adjacent in the storage array. + /// The components are said to be stored in column major order. + /// + /// The type of elements in the matrix. + public readonly struct DenseMatrix : IEquatable> + where T : struct, IEquatable + { + /// + /// The 1D representation of the dense matrix. + /// + public readonly T[] Data; + + /// + /// Gets the number of columns in the dense matrix. + /// + public readonly int Columns; + + /// + /// Gets the number of rows in the dense matrix. + /// + public readonly int Rows; + + /// + /// Gets the number of items in the array. + /// + public readonly int Count; + + /// + /// Initializes a new instance of the struct. + /// + /// The length of each side in the matrix. + public DenseMatrix(int length) + : this(length, length) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The number of columns. + /// The number of rows. + public DenseMatrix(int columns, int rows) + { + Guard.MustBeGreaterThan(columns, 0, nameof(columns)); + Guard.MustBeGreaterThan(rows, 0, nameof(rows)); + + this.Rows = rows; + this.Columns = columns; + this.Count = columns * rows; + this.Data = new T[this.Columns * this.Rows]; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The 2D array to provide access to. + public DenseMatrix(T[,] data) + { + Guard.NotNull(data, nameof(data)); + int rows = data.GetLength(0); + int columns = data.GetLength(1); + + Guard.MustBeGreaterThan(rows, 0, nameof(this.Rows)); + Guard.MustBeGreaterThan(columns, 0, nameof(this.Columns)); + + this.Rows = rows; + this.Columns = columns; + this.Count = this.Columns * this.Rows; + this.Data = new T[this.Columns * this.Rows]; + + for (int y = 0; y < this.Rows; y++) + { + for (int x = 0; x < this.Columns; x++) + { + ref T value = ref this[y, x]; + value = data[y, x]; + } + } + } + + /// + /// Gets or sets the item at the specified position. + /// + /// The row-coordinate of the item. Must be greater than or equal to zero and less than the height of the array. + /// The column-coordinate of the item. Must be greater than or equal to zero and less than the width of the array. + /// The at the specified position. + public ref T this[int row, int column] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + this.CheckCoordinates(row, column); + return ref this.Data[(row * this.Columns) + column]; + } + } + + /// + /// Performs an implicit conversion from a to a . + /// + /// The source array. + /// + /// The representation on the source data. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator DenseMatrix(T[,] data) => new DenseMatrix(data); + + /// + /// Performs an implicit conversion from a to a . + /// + /// The source array. + /// + /// The representation on the source data. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#pragma warning disable SA1008 // Opening parenthesis should be spaced correctly + public static implicit operator T[,] (DenseMatrix data) +#pragma warning restore SA1008 // Opening parenthesis should be spaced correctly + { + var result = new T[data.Rows, data.Columns]; + + for (int y = 0; y < data.Rows; y++) + { + for (int x = 0; x < data.Columns; x++) + { + ref T value = ref result[y, x]; + value = data[y, x]; + } + } + + return result; + } + + /// + /// Fills the matrix with the given value + /// + /// The value to fill each item with + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Fill(T value) + { + for (int i = 0; i < this.Data.Length; i++) + { + this.Data[i] = value; + } + } + + /// + /// Clears the matrix setting each value to the default value for the element type + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() => Array.Clear(this.Data, 0, this.Data.Length); + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the matrix. + /// The x-coordinate of the item. Must be greater than zero and smaller than the width of the matrix. + /// + /// Thrown if the coordinates are not within the bounds of the array. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int row, int column) + { + if (row < 0 || row >= this.Rows) + { + throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the matrix bounds."); + } + + if (column < 0 || column >= this.Columns) + { + throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the matrix bounds."); + } + } + + /// + public bool Equals(DenseMatrix other) + { + if (this.Columns != other.Columns) + { + return false; + } + + if (this.Rows != other.Rows) + { + return false; + } + + for (int i = 0; i < this.Data.Length; i++) + { + if (!this.Data[i].Equals(other.Data[i])) + { + return false; + } + } + + return true; + } + + /// + public override bool Equals(object obj) => obj is DenseMatrix matrix && this.Equals(matrix); + + /// + public override int GetHashCode() => this.Data.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/ValueSize.cs b/src/ImageSharp/Primitives/ValueSize.cs similarity index 90% rename from src/ImageSharp/Numerics/ValueSize.cs rename to src/ImageSharp/Primitives/ValueSize.cs index 02573816d..e64d838e6 100644 --- a/src/ImageSharp/Numerics/ValueSize.cs +++ b/src/ImageSharp/Primitives/ValueSize.cs @@ -4,7 +4,7 @@ using System; using SixLabors.Primitives; -namespace SixLabors.ImageSharp +namespace SixLabors.ImageSharp.Primitives { /// /// Represents a value in relation to a value on the image @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp } /// - /// The different vlaue types + /// Enumerates the different value types /// public enum ValueSizeType { @@ -120,10 +120,22 @@ namespace SixLabors.ImageSharp return $"{this.Value} - {this.Type}"; } + /// + public override bool Equals(object obj) + { + return obj is ValueSize size && this.Equals(size); + } + /// public bool Equals(ValueSize other) { return this.Type == other.Type && this.Value.Equals(other.Value); } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine(this.Value.GetHashCode(), this.Type.GetHashCode()); + } } } diff --git a/src/ImageSharp/Processing/Convolution/Processors/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/BoxBlurProcessor.cs index df3d56bda..b3e477f3c 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/BoxBlurProcessor.cs @@ -1,9 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -42,12 +41,12 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -59,13 +58,13 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// Create a 1 dimensional Box kernel. /// /// Whether to calculate a horizontal kernel. - /// The - private Fast2DArray CreateBoxKernel(bool horizontal) + /// The + private DenseMatrix CreateBoxKernel(bool horizontal) { int size = this.kernelSize; - Fast2DArray kernel = horizontal - ? new Fast2DArray(size, 1) - : new Fast2DArray(1, size); + DenseMatrix kernel = horizontal + ? new DenseMatrix(size, 1) + : new DenseMatrix(1, size); float sum = 0F; for (int i = 0; i < size; i++) diff --git a/src/ImageSharp/Processing/Convolution/Processors/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/Convolution2DProcessor.cs index 4e4698467..bbdd2f979 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/Convolution2DProcessor.cs @@ -7,9 +7,10 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Processors +namespace SixLabors.ImageSharp.Processing.Convolution.Processors { /// /// Defines a processor that uses two one-dimensional matrices to perform convolution against an image. @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) + public Convolution2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -32,20 +33,20 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int kernelYHeight = this.KernelY.Height; - int kernelYWidth = this.KernelY.Width; - int kernelXHeight = this.KernelX.Height; - int kernelXWidth = this.KernelX.Width; + int kernelYHeight = this.KernelY.Rows; + int kernelYWidth = this.KernelY.Columns; + int kernelXHeight = this.KernelX.Rows; + int kernelXWidth = this.KernelX.Columns; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; diff --git a/src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs index 3ea025512..51479929f 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs @@ -4,13 +4,13 @@ using System; using System.Numerics; using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Processors +namespace SixLabors.ImageSharp.Processing.Convolution.Processors { /// /// Defines a processor that uses two one-dimensional matrices to perform two-pass convolution against an image. @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2PassProcessor(Fast2DArray kernelX, Fast2DArray kernelY) + public Convolution2PassProcessor(DenseMatrix kernelX, DenseMatrix kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -33,12 +33,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -67,11 +67,11 @@ namespace SixLabors.ImageSharp.Processing.Processors Buffer2D targetPixels, Buffer2D sourcePixels, Rectangle sourceRectangle, - Fast2DArray kernel, + DenseMatrix kernel, ParallelOptions parallelOptions) { - int kernelHeight = kernel.Height; - int kernelWidth = kernel.Width; + int kernelHeight = kernel.Rows; + int kernelWidth = kernel.Columns; int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; diff --git a/src/ImageSharp/Processing/Convolution/Processors/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/ConvolutionProcessor.cs index 2eff3bf21..eb65091fe 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/ConvolutionProcessor.cs @@ -8,9 +8,10 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Processors +namespace SixLabors.ImageSharp.Processing.Convolution.Processors { /// /// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image. @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The 2d gradient operator. - public ConvolutionProcessor(Fast2DArray kernelXY) + public ConvolutionProcessor(DenseMatrix kernelXY) { this.KernelXY = kernelXY; } @@ -31,12 +32,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Gets the 2d gradient operator. /// - public Fast2DArray KernelXY { get; } + public DenseMatrix KernelXY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int kernelLength = this.KernelXY.Height; + int kernelLength = this.KernelXY.Rows; int radius = kernelLength >> 1; int startY = sourceRectangle.Y; diff --git a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetector2DProcessor.cs index 74a019445..d853fdb8e 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetector2DProcessor.cs @@ -1,10 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Filters.Processors; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -22,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetector2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY, bool grayscale) + protected EdgeDetector2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY, bool grayscale) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -32,12 +31,12 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// public bool Grayscale { get; set; } diff --git a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs index df26c23b8..b2f78fc85 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorCompassProcessor.cs @@ -4,10 +4,9 @@ using System; using System.Numerics; using System.Threading.Tasks; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Filters.Processors; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -31,42 +30,42 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the North gradient operator /// - public abstract Fast2DArray North { get; } + public abstract DenseMatrix North { get; } /// /// Gets the NorthWest gradient operator /// - public abstract Fast2DArray NorthWest { get; } + public abstract DenseMatrix NorthWest { get; } /// /// Gets the West gradient operator /// - public abstract Fast2DArray West { get; } + public abstract DenseMatrix West { get; } /// /// Gets the SouthWest gradient operator /// - public abstract Fast2DArray SouthWest { get; } + public abstract DenseMatrix SouthWest { get; } /// /// Gets the South gradient operator /// - public abstract Fast2DArray South { get; } + public abstract DenseMatrix South { get; } /// /// Gets the SouthEast gradient operator /// - public abstract Fast2DArray SouthEast { get; } + public abstract DenseMatrix SouthEast { get; } /// /// Gets the East gradient operator /// - public abstract Fast2DArray East { get; } + public abstract DenseMatrix East { get; } /// /// Gets the NorthEast gradient operator /// - public abstract Fast2DArray NorthEast { get; } + public abstract DenseMatrix NorthEast { get; } /// public bool Grayscale { get; } @@ -83,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; + DenseMatrix[] 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; diff --git a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorProcessor.cs index cd2e7d76c..ecb6364a8 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/EdgeDetectorProcessor.cs @@ -1,10 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Filters.Processors; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// The 2d gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorProcessor(Fast2DArray kernelXY, bool grayscale) + protected EdgeDetectorProcessor(DenseMatrix kernelXY, bool grayscale) { this.KernelXY = kernelXY; this.Grayscale = grayscale; @@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the 2d gradient operator. /// - public Fast2DArray KernelXY { get; } + public DenseMatrix KernelXY { get; } /// protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/src/ImageSharp/Processing/Convolution/Processors/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/GaussianBlurProcessor.cs index 3ce321cd3..328f57235 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/GaussianBlurProcessor.cs @@ -2,9 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -73,12 +72,12 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -90,14 +89,14 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private Fast2DArray CreateGaussianKernel(bool horizontal) + /// The + private DenseMatrix CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.Sigma; - Fast2DArray kernel = horizontal - ? new Fast2DArray(size, 1) - : new Fast2DArray(1, size); + DenseMatrix kernel = horizontal + ? new DenseMatrix(size, 1) + : new DenseMatrix(1, size); float sum = 0F; float midpoint = (size - 1) / 2F; diff --git a/src/ImageSharp/Processing/Convolution/Processors/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/GaussianSharpenProcessor.cs index cff0767b1..df5026db0 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/GaussianSharpenProcessor.cs @@ -2,9 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors @@ -75,12 +74,12 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public Fast2DArray KernelX { get; } + public DenseMatrix KernelX { get; } /// /// Gets the vertical gradient operator. /// - public Fast2DArray KernelY { get; } + public DenseMatrix KernelY { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -92,14 +91,14 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private Fast2DArray CreateGaussianKernel(bool horizontal) + /// The + private DenseMatrix CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.Sigma; - Fast2DArray kernel = horizontal - ? new Fast2DArray(size, 1) - : new Fast2DArray(1, size); + DenseMatrix kernel = horizontal + ? new DenseMatrix(size, 1) + : new DenseMatrix(1, size); float sum = 0; diff --git a/src/ImageSharp/Processing/Convolution/Processors/KayyaliKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/KayyaliKernels.cs index b18bd52af..e131cac38 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/KayyaliKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/KayyaliKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public static Fast2DArray KayyaliX => + public static DenseMatrix KayyaliX => new float[,] { { 6, 0, -6 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the vertical gradient operator. /// - public static Fast2DArray KayyaliY => + public static DenseMatrix KayyaliY => new float[,] { { -6, 0, 6 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/KirschProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/KirschProcessor.cs index 3ae333e18..c9a21da0b 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/KirschProcessor.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -23,27 +23,27 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors } /// - public override Fast2DArray North => KirshKernels.KirschNorth; + public override DenseMatrix North => KirshKernels.KirschNorth; /// - public override Fast2DArray NorthWest => KirshKernels.KirschNorthWest; + public override DenseMatrix NorthWest => KirshKernels.KirschNorthWest; /// - public override Fast2DArray West => KirshKernels.KirschWest; + public override DenseMatrix West => KirshKernels.KirschWest; /// - public override Fast2DArray SouthWest => KirshKernels.KirschSouthWest; + public override DenseMatrix SouthWest => KirshKernels.KirschSouthWest; /// - public override Fast2DArray South => KirshKernels.KirschSouth; + public override DenseMatrix South => KirshKernels.KirschSouth; /// - public override Fast2DArray SouthEast => KirshKernels.KirschSouthEast; + public override DenseMatrix SouthEast => KirshKernels.KirschSouthEast; /// - public override Fast2DArray East => KirshKernels.KirschEast; + public override DenseMatrix East => KirshKernels.KirschEast; /// - public override Fast2DArray NorthEast => KirshKernels.KirschNorthEast; + public override DenseMatrix NorthEast => KirshKernels.KirschNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/Processors/KirshKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/KirshKernels.cs index 99c327055..8e52f8df4 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/KirshKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/KirshKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the North gradient operator /// - public static Fast2DArray KirschNorth => + public static DenseMatrix KirschNorth => new float[,] { { 5, 5, 5 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the NorthWest gradient operator /// - public static Fast2DArray KirschNorthWest => + public static DenseMatrix KirschNorthWest => new float[,] { { 5, 5, -3 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the West gradient operator /// - public static Fast2DArray KirschWest => + public static DenseMatrix KirschWest => new float[,] { { 5, -3, -3 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the SouthWest gradient operator /// - public static Fast2DArray KirschSouthWest => + public static DenseMatrix KirschSouthWest => new float[,] { { -3, -3, -3 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the South gradient operator /// - public static Fast2DArray KirschSouth => + public static DenseMatrix KirschSouth => new float[,] { { -3, -3, -3 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the SouthEast gradient operator /// - public static Fast2DArray KirschSouthEast => + public static DenseMatrix KirschSouthEast => new float[,] { { -3, -3, -3 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the East gradient operator /// - public static Fast2DArray KirschEast => + public static DenseMatrix KirschEast => new float[,] { { -3, -3, 5 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the NorthEast gradient operator /// - public static Fast2DArray KirschNorthEast => + public static DenseMatrix KirschNorthEast => new float[,] { { -3, 5, 5 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernelFactory.cs index e28da14b5..053033432 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernelFactory.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernelFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -15,21 +15,14 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// /// The length of the matrix sides - /// The - public static Fast2DArray CreateKernel(uint length) + /// The + public static DenseMatrix CreateKernel(uint length) { Guard.MustBeGreaterThanOrEqualTo(length, 3u, nameof(length)); Guard.IsFalse(length % 2 == 0, nameof(length), "The kernel length must be an odd number."); - var kernel = new Fast2DArray((int)length); - - for (int y = 0; y < kernel.Height; y++) - { - for (int x = 0; x < kernel.Width; x++) - { - kernel[x, y] = -1; - } - } + var kernel = new DenseMatrix((int)length); + kernel.Fill(-1); int mid = (int)(length / 2); kernel[mid, mid] = (length * length) - 1; diff --git a/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernels.cs index 611982b9a..407736980 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/LaplacianKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,17 +13,17 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the 3x3 Laplacian kernel /// - public static Fast2DArray Laplacian3x3 => LaplacianKernelFactory.CreateKernel(3); + public static DenseMatrix Laplacian3x3 => LaplacianKernelFactory.CreateKernel(3); /// /// Gets the 5x5 Laplacian kernel /// - public static Fast2DArray Laplacian5x5 => LaplacianKernelFactory.CreateKernel(5); + public static DenseMatrix Laplacian5x5 => LaplacianKernelFactory.CreateKernel(5); /// /// Gets the Laplacian of Gaussian kernel. /// - public static Fast2DArray LaplacianOfGaussianXY => + public static DenseMatrix LaplacianOfGaussianXY => new float[,] { { 0, 0, -1, 0, 0 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/PrewittKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/PrewittKernels.cs index 64bce4f8c..aba4d52c3 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/PrewittKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/PrewittKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public static Fast2DArray PrewittX => + public static DenseMatrix PrewittX => new float[,] { { -1, 0, 1 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the vertical gradient operator. /// - public static Fast2DArray PrewittY => + public static DenseMatrix PrewittY => new float[,] { { 1, 1, 1 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/RobertsCrossKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/RobertsCrossKernels.cs index 63d96c79b..64d1fcea5 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/RobertsCrossKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/RobertsCrossKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public static Fast2DArray RobertsCrossX => + public static DenseMatrix RobertsCrossX => new float[,] { { 1, 0 }, @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the vertical gradient operator. /// - public static Fast2DArray RobertsCrossY => + public static DenseMatrix RobertsCrossY => new float[,] { { 0, 1 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/RobinsonKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/RobinsonKernels.cs index e10202329..9d440fcc0 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/RobinsonKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the North gradient operator /// - public static Fast2DArray RobinsonNorth => + public static DenseMatrix RobinsonNorth => new float[,] { { 1, 2, 1 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the NorthWest gradient operator /// - public static Fast2DArray RobinsonNorthWest => + public static DenseMatrix RobinsonNorthWest => new float[,] { { 2, 1, 0 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the West gradient operator /// - public static Fast2DArray RobinsonWest => + public static DenseMatrix RobinsonWest => new float[,] { { 1, 0, -1 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the SouthWest gradient operator /// - public static Fast2DArray RobinsonSouthWest => + public static DenseMatrix RobinsonSouthWest => new float[,] { { 0, -1, -2 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the South gradient operator /// - public static Fast2DArray RobinsonSouth => + public static DenseMatrix RobinsonSouth => new float[,] { { -1, -2, -1 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the SouthEast gradient operator /// - public static Fast2DArray RobinsonSouthEast => + public static DenseMatrix RobinsonSouthEast => new float[,] { { -2, -1, 0 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the East gradient operator /// - public static Fast2DArray RobinsonEast => + public static DenseMatrix RobinsonEast => new float[,] { { -1, 0, 1 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the NorthEast gradient operator /// - public static Fast2DArray RobinsonNorthEast => + public static DenseMatrix RobinsonNorthEast => new float[,] { { 0, 1, 2 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/RobinsonProcessor.cs b/src/ImageSharp/Processing/Convolution/Processors/RobinsonProcessor.cs index fac0c52e5..f129b1daa 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/RobinsonProcessor.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -24,27 +24,27 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors } /// - public override Fast2DArray North => RobinsonKernels.RobinsonNorth; + public override DenseMatrix North => RobinsonKernels.RobinsonNorth; /// - public override Fast2DArray NorthWest => RobinsonKernels.RobinsonNorthWest; + public override DenseMatrix NorthWest => RobinsonKernels.RobinsonNorthWest; /// - public override Fast2DArray West => RobinsonKernels.RobinsonWest; + public override DenseMatrix West => RobinsonKernels.RobinsonWest; /// - public override Fast2DArray SouthWest => RobinsonKernels.RobinsonSouthWest; + public override DenseMatrix SouthWest => RobinsonKernels.RobinsonSouthWest; /// - public override Fast2DArray South => RobinsonKernels.RobinsonSouth; + public override DenseMatrix South => RobinsonKernels.RobinsonSouth; /// - public override Fast2DArray SouthEast => RobinsonKernels.RobinsonSouthEast; + public override DenseMatrix SouthEast => RobinsonKernels.RobinsonSouthEast; /// - public override Fast2DArray East => RobinsonKernels.RobinsonEast; + public override DenseMatrix East => RobinsonKernels.RobinsonEast; /// - public override Fast2DArray NorthEast => RobinsonKernels.RobinsonNorthEast; + public override DenseMatrix NorthEast => RobinsonKernels.RobinsonNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/Processors/ScharrKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/ScharrKernels.cs index cf8b9925a..c309e4cec 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/ScharrKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/ScharrKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public static Fast2DArray ScharrX => + public static DenseMatrix ScharrX => new float[,] { { -3, 0, 3 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the vertical gradient operator. /// - public static Fast2DArray ScharrY => + public static DenseMatrix ScharrY => new float[,] { { 3, 10, 3 }, diff --git a/src/ImageSharp/Processing/Convolution/Processors/SobelKernels.cs b/src/ImageSharp/Processing/Convolution/Processors/SobelKernels.cs index 691bd6614..626226c66 100644 --- a/src/ImageSharp/Processing/Convolution/Processors/SobelKernels.cs +++ b/src/ImageSharp/Processing/Convolution/Processors/SobelKernels.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Convolution.Processors { @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the horizontal gradient operator. /// - public static Fast2DArray SobelX => + public static DenseMatrix SobelX => new float[,] { { -1, 0, 1 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors /// /// Gets the vertical gradient operator. /// - public static Fast2DArray SobelY => + public static DenseMatrix SobelY => new float[,] { { -1, -2, -1 }, diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index af80666d6..80eb4287a 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index ba2424d77..4df53f14e 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index d08283a0f..abd0c15bb 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 3a2fc595c..ad73b6553 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index 02df1a19e..60d89847f 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -9,7 +9,18 @@ namespace SixLabors.ImageSharp.Benchmarks.General using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Primitives; + + /** + * Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | +-------------------------------------------- |------ |---------:|---------:|---------:|-------:|---------:| + 'Emulated 2D array access using flat array' | 32 | 224.2 ns | 4.739 ns | 13.75 ns | 0.65 | 0.07 | + 'Array access using 2D array' | 32 | 346.6 ns | 9.225 ns | 26.91 ns | 1.00 | 0.00 | + 'Array access using a jagged array' | 32 | 229.3 ns | 6.028 ns | 17.58 ns | 0.67 | 0.07 | + 'Array access using DenseMatrix' | 32 | 223.2 ns | 5.248 ns | 15.22 ns | 0.65 | 0.07 | + + * + */ public class Array2D { @@ -19,9 +30,9 @@ namespace SixLabors.ImageSharp.Benchmarks.General private float[][] jaggedData; - private Fast2DArray fastData; - - [Params(4, 16, 128)] + private DenseMatrix matrix; + + [Params(4, 16, 32)] public int Count { get; set; } public int Min { get; private set; } @@ -39,9 +50,9 @@ namespace SixLabors.ImageSharp.Benchmarks.General this.jaggedData[i] = new float[this.Count]; } - this.fastData = new Fast2DArray(this.array2D); + this.matrix = new DenseMatrix(this.array2D); - this.Min = this.Count / 2 - 10; + this.Min = (this.Count / 2) - 10; this.Min = Math.Max(0, this.Min); this.Max = this.Min + Math.Min(10, this.Count); } @@ -56,7 +67,9 @@ namespace SixLabors.ImageSharp.Benchmarks.General { for (int j = this.Min; j < this.Max; j++) { - s += a[count * i + j]; + ref float v = ref a[count * i + j]; + v = i * j; + s += v; } } return s; @@ -71,7 +84,9 @@ namespace SixLabors.ImageSharp.Benchmarks.General { for (int j = this.Min; j < this.Max; j++) { - s += a[i, j]; + ref float v = ref a[i, j]; + v = i * j; + s += v; } } return s; @@ -86,25 +101,29 @@ namespace SixLabors.ImageSharp.Benchmarks.General { for (int j = this.Min; j < this.Max; j++) { - s += a[i][j]; + ref float v = ref a[i][j]; + v = i * j; + s += v; } } return s; } - [Benchmark(Description = "Array access using Fast2DArray")] - public float ArrayFastIndex() + [Benchmark(Description = "Array access using DenseMatrix")] + public float ArrayMatrixIndex() { float s = 0; - Fast2DArray a = this.fastData; + DenseMatrix a = this.matrix; for (int i = this.Min; i < this.Max; i++) { for (int j = this.Min; j < this.Max; j++) { - s += a[i, j]; + ref float v = ref a[i, j]; + v = i * j; + s += v; } } return s; } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 70badd34c..4c6cc7acb 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing public void FilledBezier(TestImageProvider provider) where TPixel : struct, IPixel { - Primitives.PointF[] simplePath = { + SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing public void OverlayByFilledPolygonOpacity(TestImageProvider provider) where TPixel : struct, IPixel { - Primitives.PointF[] simplePath = { + SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), diff --git a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs new file mode 100644 index 000000000..7d161d35f --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Primitives +{ + public class DenseMatrixTests + { + private static readonly float[,] FloydSteinbergMatrix = + { + { 0, 0, 7 }, + { 3, 5, 1 } + }; + + [Fact] + public void DenseMatrixThrowsOnNullInitializer() + { + Assert.Throws(() => + { + var dense = new DenseMatrix(null); + }); + } + + [Fact] + public void DenseMatrixThrowsOnEmptyZeroWidth() + { + Assert.Throws(() => + { + var dense = new DenseMatrix(0, 10); + }); + } + + [Fact] + public void DenseMatrixThrowsOnEmptyZeroHeight() + { + Assert.Throws(() => + { + var dense = new DenseMatrix(10, 0); + }); + } + + [Fact] + public void DenseMatrixThrowsOnEmptyInitializer() + { + Assert.Throws(() => + { + var dense = new DenseMatrix(new float[0, 0]); + }); + } + + [Fact] + public void DenseMatrixReturnsCorrectDimensions() + { + var dense = new DenseMatrix(FloydSteinbergMatrix); + Assert.True(dense.Columns == FloydSteinbergMatrix.GetLength(1)); + Assert.True(dense.Rows == FloydSteinbergMatrix.GetLength(0)); + Assert.Equal(3, dense.Columns); + Assert.Equal(2, dense.Rows); + } + + [Fact] + public void DenseMatrixGetReturnsCorrectResults() + { + DenseMatrix dense = FloydSteinbergMatrix; + + for (int row = 0; row < dense.Rows; row++) + { + for (int column = 0; column < dense.Columns; column++) + { + Assert.True(Math.Abs(dense[row, column] - FloydSteinbergMatrix[row, column]) < Constants.Epsilon); + } + } + } + + [Fact] + public void DenseMatrixGetSetReturnsCorrectResults() + { + var dense = new DenseMatrix(4, 4); + const int Val = 5; + + dense[3, 3] = Val; + + Assert.Equal(Val, dense[3, 3]); + } + + [Fact] + public void DenseMatrixCanFillAndClear() + { + var dense = new DenseMatrix(9); + dense.Fill(4); + + for (int i = 0; i < dense.Data.Length; i++) + { + Assert.Equal(4, dense.Data[i]); + } + + dense.Clear(); + + for (int i = 0; i < dense.Data.Length; i++) + { + Assert.Equal(0, dense.Data[i]); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index b3ecdacbe..0a87f8343 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { + using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; public class GlowTest : BaseImageOperationsExtensionTest diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 01555c02a..25b7d26ad 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -10,6 +10,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { + using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; public class VignetteTest : BaseImageOperationsExtensionTest