diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 76082136c7..eab6b2f4a1 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -4,7 +4,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs index a7c22f6d7b..4e052818da 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System.Buffers; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -45,7 +45,7 @@ protected override void OnFrameApply(ImageFrame source) int width = maxX - minX; - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); IBrush brush = this.definition.Brush; GraphicsOptions options = this.definition.Options; @@ -53,9 +53,10 @@ protected override void OnFrameApply(ImageFrame source) // If there's no reason for blending, then avoid it. if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration) + .MultiplyMinimumPixelsPerTask(4); - TPixel colorPixel = solidBrush.Color.ToPixel(); + var colorPixel = solidBrush.Color.ToPixel(); ParallelHelper.IterateRows( workingRect, diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs similarity index 55% rename from src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs index 40163bc789..431656ef91 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs @@ -1,16 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Threading.Tasks; using SixLabors.Memory; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Defines execution settings for methods in . /// - internal readonly struct ParallelExecutionSettings + public readonly struct ParallelExecutionSettings { /// /// Default value for . @@ -20,11 +21,24 @@ internal readonly struct ParallelExecutionSettings /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The value for . + /// The . public ParallelExecutionSettings( int maxDegreeOfParallelism, int minimumPixelsProcessedPerTask, MemoryAllocator memoryAllocator) { + // Shall be compatible with ParallelOptions.MaxDegreeOfParallelism: + // https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions.maxdegreeofparallelism + if (maxDegreeOfParallelism == 0 || maxDegreeOfParallelism < -1) + { + throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism)); + } + + Guard.MustBeGreaterThan(minimumPixelsProcessedPerTask, 0, nameof(minimumPixelsProcessedPerTask)); + Guard.NotNull(memoryAllocator, nameof(memoryAllocator)); + this.MaxDegreeOfParallelism = maxDegreeOfParallelism; this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; this.MemoryAllocator = memoryAllocator; @@ -33,13 +47,15 @@ public ParallelExecutionSettings( /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The . public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) : this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator) { } /// - /// Gets the MemoryAllocator + /// Gets the . /// public MemoryAllocator MemoryAllocator { get; } @@ -60,12 +76,26 @@ public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator mem /// Creates a new instance of /// having multiplied by /// + /// The value to multiply with. + /// The modified . public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) { + Guard.MustBeGreaterThan(multiplier, 0, nameof(multiplier)); + return new ParallelExecutionSettings( this.MaxDegreeOfParallelism, this.MinimumPixelsProcessedPerTask * multiplier, this.MemoryAllocator); } + + /// + /// Get the default for a + /// + /// The . + /// The . + public static ParallelExecutionSettings FromConfiguration(Configuration configuration) + { + return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs similarity index 77% rename from src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs index 1e7299720d..c56337bffe 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs @@ -10,29 +10,25 @@ using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Utility methods for batched processing of pixel row intervals. - /// Parallel execution is optimized for image processing. - /// Use this instead of direct calls! + /// Parallel execution is optimized for image processing based on values defined + /// or . + /// Using this class is preferred over direct usage of utility methods. /// - internal static class ParallelHelper + public static class ParallelHelper { - /// - /// Get the default for a - /// - public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration) - { - return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); - } - /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The to get the parallel settings from. + /// The method body defining the iteration logic on a single . public static void IterateRows(Rectangle rectangle, Configuration configuration, Action body) { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); IterateRows(rectangle, parallelSettings, body); } @@ -40,6 +36,9 @@ public static void IterateRows(Rectangle rectangle, Configuration configuration, /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The . + /// The method body defining the iteration logic on a single . public static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, @@ -47,7 +46,9 @@ public static void IterateRows( { ValidateRectangle(rectangle); - int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); + int maxSteps = DivideCeil( + rectangle.Width * rectangle.Height, + parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); @@ -81,7 +82,7 @@ public static void IterateRows( /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action> body) @@ -133,13 +134,13 @@ public static void IterateRowsWithTempBuffer( /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, Configuration configuration, Action> body) where T : unmanaged { - IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); + IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0d44db8d87..ae20490c77 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -63,7 +63,7 @@ public int MaxDegreeOfParallelism get => this.maxDegreeOfParallelism; set { - if (value <= 0) + if (value == 0 || value < -1) { throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); } @@ -161,4 +161,4 @@ internal static Configuration CreateDefaultInstance() new BmpConfigurationModule()); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs index 893a7a4a80..722a4ddea8 100644 --- a/src/ImageSharp/ImageFrameCollection{TPixel}.cs +++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs @@ -351,7 +351,7 @@ private ImageFrame CopyNonCompatibleFrame(ImageFrame source) this.parent.GetConfiguration(), source.Size(), source.Metadata.DeepClone()); - source.CopyPixelsTo(result.PixelBuffer.Span); + source.CopyPixelsTo(result.PixelBuffer.GetSpan()); return result; } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 0436eb9d2b..64f37a3407 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -6,9 +6,9 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -218,10 +218,10 @@ internal override void CopyPixelsTo(Span d if (typeof(TPixel) == typeof(TDestinationPixel)) { Span dest1 = MemoryMarshal.Cast(destination); - this.PixelBuffer.Span.CopyTo(dest1); + this.PixelBuffer.GetSpan().CopyTo(dest1); } - PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.Span, destination); + PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.GetSpan(), destination); } /// diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 59247aa2d2..35d55ba590 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -13,13 +13,69 @@ namespace SixLabors.ImageSharp.Memory /// /// Defines extension methods for . /// - internal static class Buffer2DExtensions + public static class Buffer2DExtensions { + /// + /// Gets a to the backing buffer of . + /// + /// The . + /// The value type. + /// The referencing the memory area. + public static Span GetSpan(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.GetSpan(); + } + + /// + /// Gets the holding the backing buffer of . + /// + /// The . + /// The value type. + /// The . + public static Memory GetMemory(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory; + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory GetRowMemory(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); + } + /// /// Copy columns of inplace, /// from positions starting at to positions at . /// - public static unsafe void CopyColumns( + internal static unsafe void CopyColumns( this Buffer2D buffer, int sourceIndex, int destIndex, @@ -37,7 +93,7 @@ public static unsafe void CopyColumns( int dOffset = destIndex * elementSize; long count = columnCount * elementSize; - Span span = MemoryMarshal.AsBytes(buffer.Memory.Span); + Span span = MemoryMarshal.AsBytes(buffer.GetMemory().Span); fixed (byte* ptr = span) { @@ -60,7 +116,7 @@ public static unsafe void CopyColumns( /// The element type /// The /// The - public static Rectangle FullRectangle(this Buffer2D buffer) + internal static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { return new Rectangle(0, 0, buffer.Width, buffer.Height); @@ -73,11 +129,11 @@ public static Rectangle FullRectangle(this Buffer2D buffer) /// The /// The rectangle subarea /// The - public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) + internal static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) where T : struct => new BufferArea(buffer, rectangle); - public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) + internal static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); @@ -87,64 +143,17 @@ public static BufferArea GetArea(this Buffer2D buffer, int x, int y, in /// The element type /// The /// The - public static BufferArea GetArea(this Buffer2D buffer) + internal static BufferArea GetArea(this Buffer2D buffer) where T : struct => new BufferArea(buffer); - public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) - where T : struct => - new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); - /// /// Gets a span for all the pixels in defined by /// - public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) + internal static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory GetRowMemory(this Buffer2D buffer, int y) - where T : struct - { - return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. - /// - /// The buffer - /// The x coordinate (position in the row) - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int x, int y) - where T : struct - { - return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) - where T : struct - { - return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + return buffer.GetSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// @@ -153,21 +162,12 @@ public static Span GetRowSpan(this Buffer2D buffer, int y) /// The element type /// The /// The of the buffer - public static Size Size(this Buffer2D buffer) + internal static Size Size(this Buffer2D buffer) where T : struct { return new Size(buffer.Width, buffer.Height); } - /// - /// Gets a to the backing buffer of . - /// - internal static Span GetSpan(this Buffer2D buffer) - where T : struct - { - return buffer.MemorySource.GetSpan(); - } - [Conditional("DEBUG")] private static void CheckColumnRegionsDoNotOverlap( Buffer2D buffer, diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 82a98bfc63..06cfdf5607 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -12,8 +12,12 @@ namespace SixLabors.ImageSharp.Memory /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// /// The value type. - internal sealed class Buffer2D : IDisposable + // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core). + public sealed class Buffer2D : IDisposable where T : struct { private MemorySource memorySource; @@ -24,7 +28,7 @@ internal sealed class Buffer2D : IDisposable /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(MemorySource memorySource, int width, int height) + internal Buffer2D(MemorySource memorySource, int width, int height) { this.memorySource = memorySource; this.Width = width; @@ -44,11 +48,7 @@ public Buffer2D(MemorySource memorySource, int width, int height) /// /// Gets the backing /// - public MemorySource MemorySource => this.memorySource; - - public Memory Memory => this.MemorySource.Memory; - - public Span Span => this.Memory.Span; + internal MemorySource MemorySource => this.memorySource; /// /// Gets a reference to the element at the specified position. @@ -56,7 +56,7 @@ public Buffer2D(MemorySource memorySource, int width, int height) /// The x coordinate (row) /// The y coordinate (position at row) /// A reference to the element. - public ref T this[int x, int y] + internal ref T this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -64,7 +64,7 @@ public Buffer2D(MemorySource memorySource, int width, int height) DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - Span span = this.Span; + Span span = this.GetSpan(); return ref span[(this.Width * y) + x]; } } @@ -81,7 +81,7 @@ public Buffer2D Slice(int y, int h) DebugGuard.MustBeGreaterThan(h, 0, nameof(h)); DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h)); - Memory slice = this.Memory.Slice(y * this.Width, h * this.Width); + Memory slice = this.GetMemory().Slice(y * this.Width, h * this.Width); var memory = new MemorySource(slice); return new Buffer2D(memory, this.Width, h); } @@ -98,7 +98,7 @@ public void Dispose() /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! /// - public static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) + internal static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { MemorySource.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource); SwapDimensionData(destination, source); diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 815918754a..13037c889f 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -10,26 +10,32 @@ namespace SixLabors.ImageSharp.Memory /// /// Represents an interval of rows in a and/or /// - internal readonly struct RowInterval : IEquatable + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// + // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core). + public readonly struct RowInterval : IEquatable { /// /// Initializes a new instance of the struct. /// + /// The inclusive minimum row. + /// The exclusive maximum row. public RowInterval(int min, int max) { - DebugGuard.MustBeLessThan(min, max, nameof(min)); + Guard.MustBeLessThan(min, max, nameof(min)); this.Min = min; this.Max = max; } /// - /// Gets the INCLUSIVE minimum. + /// Gets the inclusive minimum row. /// public int Min { get; } /// - /// Gets the EXCLUSIVE maximum. + /// Gets the exclusive maximum row. /// public int Max { get; } @@ -38,33 +44,48 @@ public RowInterval(int min, int max) /// public int Height => this.Max - this.Min; + /// + /// Returns a boolean indicating whether the given two -s are equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are equal; False otherwise. public static bool operator ==(RowInterval left, RowInterval right) { return left.Equals(right); } + /// + /// Returns a boolean indicating whether the given two -s are not equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are not equal; False otherwise. public static bool operator !=(RowInterval left, RowInterval right) { return !left.Equals(right); } /// - public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; - - public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); - - public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); - public bool Equals(RowInterval other) { return this.Min == other.Min && this.Max == other.Max; } + /// public override bool Equals(object obj) { return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); } + /// public override int GetHashCode() => HashCode.Combine(this.Min, this.Max); + + /// + public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; + + internal RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); + + internal RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 7234955edb..7208fc3cd5 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -4,7 +4,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 9339c8fe43..f8fb3f796d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -8,8 +8,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index b38d87cd4f..4419f064e5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System.Numerics; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index a523f7c227..9fad8b5b73 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -4,8 +4,9 @@ using System; using System.Numerics; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 5bdec738d7..f657e131dd 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -4,8 +4,9 @@ using System; using System.Numerics; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index dc9974c616..4fa87bc98e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -6,8 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index ea7ba7409d..4cac6b0f66 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 5fa233d180..731cd2a052 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 8aaa5403d4..f8cd5620a2 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -8,8 +8,8 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index 2459b47069..7e3d2fdc5b 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System.Buffers; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 756e8647ba..4082d9a65b 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -6,8 +6,8 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 8569410d22..c27a634f3d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -6,8 +6,8 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 97b8b009b5..e693de8f6e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 1bbdd0a161..2cc4a38675 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -3,7 +3,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -47,10 +47,8 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); - this.pinHandle = this.data.Memory.Pin(); + this.pinHandle = this.data.GetMemory().Pin(); this.kernels = new ResizeKernel[destinationLength]; this.tempValues = new double[this.MaxDiameter]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 78e471ad62..3b508032a0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -4,8 +4,8 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 00a8cfbf3d..8461272757 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -115,6 +115,7 @@ public void Initialize() public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span tempColSpan = this.tempColumnBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); for (int y = rowInterval.Min; y < rowInterval.Max; y++) { @@ -129,8 +130,7 @@ public void FillDestinationPixels(RowInterval rowInterval, Buffer2D dest ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); int top = kernel.StartIndex - this.currentWindow.Min; - - ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top]; + ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top]; for (int x = 0; x < this.destWidth; x++) { @@ -167,6 +167,8 @@ private void Slide() private void CalculateFirstPassValues(RowInterval calculationInterval) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); + for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -177,17 +179,19 @@ private void CalculateFirstPassValues(RowInterval calculationInterval) tempRowSpan, this.conversionModifiers); - // Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); - ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; + // optimization for: + // Span firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min); + ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min]; for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); + // optimization for: // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index 1ed4c362c5..92776b7db5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -4,8 +4,8 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 6f68d04288..a0e552aebf 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -61,12 +61,28 @@ public void TestDefaultConfigurationMaxDegreeOfParallelism() } [Theory] - [InlineData(0)] - [InlineData(-42)] - public void Set_MaxDegreeOfParallelism_ToNonPositiveValue_Throws(int value) + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) { var cfg = new Configuration(); - Assert.Throws(() => cfg.MaxDegreeOfParallelism = value); + if (throws) + { + Assert.Throws( + () => + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + }); + } + else + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + Assert.Equal(maxDegreeOfParallelism, cfg.MaxDegreeOfParallelism); + } } @@ -117,4 +133,4 @@ public void WorkingBufferSizeHint_DefaultIsCorrect() Assert.True(config.WorkingBufferSizeHintInBytes > 1024); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs new file mode 100644 index 0000000000..3cfce6b8e2 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced.ParallelUtils; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class ParallelExecutionSettingsTests + { + [Theory] + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) + { + if (throws) + { + Assert.Throws( + () => + { + _ = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + }); + } + else + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index aeadfcebb5..4b5c87c7f5 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -7,8 +7,8 @@ using System.Numerics; using System.Threading; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -332,7 +332,7 @@ void FillRow(int y, Buffer2D buffer) }); // Assert: - TestImageExtensions.CompareBuffers(expected.Span, actual.Span); + TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan()); } } diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 45067f82fe..0bb3f49d64 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -25,7 +25,7 @@ public void GetMultiRowSpan(int width, int height, int min, int max) Span span = buffer.GetMultiRowSpan(rows); - ref int expected0 = ref buffer.Span[min * width]; + ref int expected0 = ref buffer.GetSpan()[min * width]; int expectedLength = (max - min) * width; ref int actual0 = ref span[0]; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index ee32be3ca7..a0e4f54ac8 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -42,7 +42,7 @@ public void Construct(int width, int height) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Memory.Length); + Assert.Equal(width * height, buffer.GetMemory().Length); } } @@ -75,22 +75,6 @@ public void GetRowSpanY(int width, int height, int y) } } - [Theory] - [InlineData(7, 42, 0, 0)] - [InlineData(7, 42, 3, 10)] - [InlineData(17, 42, 0, 41)] - public void GetRowSpanXY(int width, int height, int x, int y) - { - using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) - { - Span span = buffer.GetRowSpan(x, y); - - // Assert.Equal(width * y + x, span.Start); - Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x); - } - } - [Theory] [InlineData(42, 8, 0, 0)] [InlineData(400, 1000, 20, 10)] @@ -140,7 +124,7 @@ public void CopyColumns(int width, int height, int startIndex, int destIndex, in var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(startIndex, destIndex, columnCount); @@ -162,7 +146,7 @@ public void CopyColumns_InvokeMultipleTimes() var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 95f7f81fda..9da39fbe40 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -7,9 +7,9 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors;