mirror of https://github.com/SixLabors/ImageSharp
54 changed files with 2974 additions and 2022 deletions
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the contract for an action that operates on a row interval.
|
||||
|
/// </summary>
|
||||
|
public interface IRowIntervalOperation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Invokes the method passing the row interval.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rows">The row interval.</param>
|
||||
|
void Invoke(in RowInterval rows); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the contract for an action that operates on a row interval with a temporary buffer.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
|
||||
|
public interface IRowIntervalOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Invokes the method passing the row interval and a buffer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rows">The row interval.</param>
|
||||
|
/// <param name="span">The contiguous region of memory.</param>
|
||||
|
void Invoke(in RowInterval rows, Span<TBuffer> span); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,110 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Threading.Tasks; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <content>
|
||||
|
/// Utility methods for batched processing of pixel row intervals.
|
||||
|
/// Parallel execution is optimized for image processing based on values defined
|
||||
|
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
|
||||
|
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
|
||||
|
/// </content>
|
||||
|
public static partial class ParallelRowIterator |
||||
|
{ |
||||
|
private readonly struct IterationParameters |
||||
|
{ |
||||
|
public readonly int MinY; |
||||
|
public readonly int MaxY; |
||||
|
public readonly int StepY; |
||||
|
public readonly int Width; |
||||
|
|
||||
|
public IterationParameters(int minY, int maxY, int stepY) |
||||
|
: this(minY, maxY, stepY, 0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public IterationParameters(int minY, int maxY, int stepY, int width) |
||||
|
{ |
||||
|
this.MinY = minY; |
||||
|
this.MaxY = maxY; |
||||
|
this.StepY = stepY; |
||||
|
this.Width = width; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly struct RowIntervalOperationWrapper<T> |
||||
|
where T : struct, IRowIntervalOperation |
||||
|
{ |
||||
|
private readonly IterationParameters info; |
||||
|
private readonly T operation; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowIntervalOperationWrapper(in IterationParameters info, in T operation) |
||||
|
{ |
||||
|
this.info = info; |
||||
|
this.operation = operation; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.info.MinY + (i * this.info.StepY); |
||||
|
|
||||
|
if (yMin >= this.info.MaxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY); |
||||
|
var rows = new RowInterval(yMin, yMax); |
||||
|
|
||||
|
// Skip the safety copy when invoking a potentially impure method on a readonly field
|
||||
|
Unsafe.AsRef(in this.operation).Invoke(in rows); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly struct RowIntervalOperationWrapper<T, TBuffer> |
||||
|
where T : struct, IRowIntervalOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
private readonly IterationParameters info; |
||||
|
private readonly MemoryAllocator allocator; |
||||
|
private readonly T operation; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowIntervalOperationWrapper( |
||||
|
in IterationParameters info, |
||||
|
MemoryAllocator allocator, |
||||
|
in T operation) |
||||
|
{ |
||||
|
this.info = info; |
||||
|
this.allocator = allocator; |
||||
|
this.operation = operation; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.info.MinY + (i * this.info.StepY); |
||||
|
|
||||
|
if (yMin >= this.info.MaxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY); |
||||
|
var rows = new RowInterval(yMin, yMax); |
||||
|
|
||||
|
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.Width); |
||||
|
|
||||
|
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Threading.Tasks; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Utility methods for batched processing of pixel row intervals.
|
||||
|
/// Parallel execution is optimized for image processing based on values defined
|
||||
|
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
|
||||
|
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
|
||||
|
/// </summary>
|
||||
|
public static partial class ParallelRowIterator |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of row operation to perform.</typeparam>
|
||||
|
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
|
||||
|
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
||||
|
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowIntervalOperation |
||||
|
{ |
||||
|
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
||||
|
IterateRows(rectangle, in parallelSettings, in operation); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of row operation to perform.</typeparam>
|
||||
|
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
||||
|
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
|
||||
|
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
||||
|
public static void IterateRows<T>( |
||||
|
Rectangle rectangle, |
||||
|
in ParallelExecutionSettings parallelSettings, |
||||
|
in T operation) |
||||
|
where T : struct, IRowIntervalOperation |
||||
|
{ |
||||
|
ValidateRectangle(rectangle); |
||||
|
|
||||
|
int top = rectangle.Top; |
||||
|
int bottom = rectangle.Bottom; |
||||
|
int width = rectangle.Width; |
||||
|
int height = rectangle.Height; |
||||
|
|
||||
|
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); |
||||
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
||||
|
|
||||
|
// Avoid TPL overhead in this trivial case:
|
||||
|
if (numOfSteps == 1) |
||||
|
{ |
||||
|
var rows = new RowInterval(top, bottom); |
||||
|
Unsafe.AsRef(in operation).Invoke(in rows); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
||||
|
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
||||
|
var info = new IterationParameters(top, bottom, verticalStep); |
||||
|
var wrappingOperation = new RowIntervalOperationWrapper<T>(in info, in operation); |
||||
|
|
||||
|
Parallel.For( |
||||
|
0, |
||||
|
numOfSteps, |
||||
|
parallelOptions, |
||||
|
wrappingOperation.Invoke); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
|
||||
|
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of row operation to perform.</typeparam>
|
||||
|
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
|
||||
|
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
|
||||
|
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
||||
|
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
||||
|
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowIntervalOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
||||
|
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in operation); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
|
||||
|
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of row operation to perform.</typeparam>
|
||||
|
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
|
||||
|
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
||||
|
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
|
||||
|
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
||||
|
public static void IterateRows<T, TBuffer>( |
||||
|
Rectangle rectangle, |
||||
|
in ParallelExecutionSettings parallelSettings, |
||||
|
in T operation) |
||||
|
where T : struct, IRowIntervalOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
ValidateRectangle(rectangle); |
||||
|
|
||||
|
int top = rectangle.Top; |
||||
|
int bottom = rectangle.Bottom; |
||||
|
int width = rectangle.Width; |
||||
|
int height = rectangle.Height; |
||||
|
|
||||
|
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask); |
||||
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
||||
|
MemoryAllocator allocator = parallelSettings.MemoryAllocator; |
||||
|
|
||||
|
// Avoid TPL overhead in this trivial case:
|
||||
|
if (numOfSteps == 1) |
||||
|
{ |
||||
|
var rows = new RowInterval(top, bottom); |
||||
|
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width)) |
||||
|
{ |
||||
|
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int verticalStep = DivideCeil(height, numOfSteps); |
||||
|
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
||||
|
var info = new IterationParameters(top, bottom, verticalStep, width); |
||||
|
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(in info, allocator, in operation); |
||||
|
|
||||
|
Parallel.For( |
||||
|
0, |
||||
|
numOfSteps, |
||||
|
parallelOptions, |
||||
|
wrappingOperation.Invoke); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor); |
||||
|
|
||||
|
private static void ValidateRectangle(Rectangle rectangle) |
||||
|
{ |
||||
|
Guard.MustBeGreaterThan( |
||||
|
rectangle.Width, |
||||
|
0, |
||||
|
$"{nameof(rectangle)}.{nameof(rectangle.Width)}"); |
||||
|
|
||||
|
Guard.MustBeGreaterThan( |
||||
|
rectangle.Height, |
||||
|
0, |
||||
|
$"{nameof(rectangle)}.{nameof(rectangle.Height)}"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,178 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Advanced.ParallelUtils |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Utility methods for batched processing of pixel row intervals.
|
|
||||
/// Parallel execution is optimized for image processing based on values defined
|
|
||||
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
|
|
||||
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
|
|
||||
/// </summary>
|
|
||||
public static class ParallelHelper |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
|
||||
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
|
|
||||
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
|
||||
public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body) |
|
||||
{ |
|
||||
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
|
||||
|
|
||||
IterateRows(rectangle, parallelSettings, body); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
|
|
||||
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
|
|
||||
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
|
|
||||
public static void IterateRows( |
|
||||
Rectangle rectangle, |
|
||||
in ParallelExecutionSettings parallelSettings, |
|
||||
Action<RowInterval> body) |
|
||||
{ |
|
||||
ValidateRectangle(rectangle); |
|
||||
|
|
||||
int maxSteps = DivideCeil( |
|
||||
rectangle.Width * rectangle.Height, |
|
||||
parallelSettings.MinimumPixelsProcessedPerTask); |
|
||||
|
|
||||
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
||||
|
|
||||
// Avoid TPL overhead in this trivial case:
|
|
||||
if (numOfSteps == 1) |
|
||||
{ |
|
||||
var rows = new RowInterval(rectangle.Top, rectangle.Bottom); |
|
||||
body(rows); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
|
||||
|
|
||||
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
|
||||
|
|
||||
int top = rectangle.Top; |
|
||||
int bottom = rectangle.Bottom; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
numOfSteps, |
|
||||
parallelOptions, |
|
||||
i => |
|
||||
{ |
|
||||
int yMin = top + (i * verticalStep); |
|
||||
|
|
||||
if (yMin >= bottom) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
int yMax = Math.Min(yMin + verticalStep, bottom); |
|
||||
|
|
||||
var rows = new RowInterval(yMin, yMax); |
|
||||
body(rows); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
|
|
||||
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
|
|
||||
/// </summary>
|
|
||||
internal static void IterateRowsWithTempBuffer<T>( |
|
||||
Rectangle rectangle, |
|
||||
in ParallelExecutionSettings parallelSettings, |
|
||||
Action<RowInterval, Memory<T>> body) |
|
||||
where T : unmanaged |
|
||||
{ |
|
||||
ValidateRectangle(rectangle); |
|
||||
|
|
||||
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
||||
|
|
||||
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
||||
|
|
||||
MemoryAllocator memoryAllocator = parallelSettings.MemoryAllocator; |
|
||||
|
|
||||
// Avoid TPL overhead in this trivial case:
|
|
||||
if (numOfSteps == 1) |
|
||||
{ |
|
||||
var rows = new RowInterval(rectangle.Top, rectangle.Bottom); |
|
||||
using (IMemoryOwner<T> buffer = memoryAllocator.Allocate<T>(rectangle.Width)) |
|
||||
{ |
|
||||
body(rows, buffer.Memory); |
|
||||
} |
|
||||
|
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
|
||||
|
|
||||
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
|
||||
|
|
||||
int top = rectangle.Top; |
|
||||
int bottom = rectangle.Bottom; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
numOfSteps, |
|
||||
parallelOptions, |
|
||||
i => |
|
||||
{ |
|
||||
int yMin = top + (i * verticalStep); |
|
||||
|
|
||||
if (yMin >= bottom) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
int yMax = Math.Min(yMin + verticalStep, rectangle.Bottom); |
|
||||
|
|
||||
var rows = new RowInterval(yMin, yMax); |
|
||||
|
|
||||
using (IMemoryOwner<T> buffer = memoryAllocator.Allocate<T>(rectangle.Width)) |
|
||||
{ |
|
||||
body(rows, buffer.Memory); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
|
|
||||
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
|
|
||||
/// </summary>
|
|
||||
internal static void IterateRowsWithTempBuffer<T>( |
|
||||
Rectangle rectangle, |
|
||||
Configuration configuration, |
|
||||
Action<RowInterval, Memory<T>> body) |
|
||||
where T : unmanaged |
|
||||
{ |
|
||||
IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor); |
|
||||
|
|
||||
private static void ValidateRectangle(Rectangle rectangle) |
|
||||
{ |
|
||||
Guard.MustBeGreaterThan( |
|
||||
rectangle.Width, |
|
||||
0, |
|
||||
$"{nameof(rectangle)}.{nameof(rectangle.Width)}"); |
|
||||
|
|
||||
Guard.MustBeGreaterThan( |
|
||||
rectangle.Height, |
|
||||
0, |
|
||||
$"{nameof(rectangle)}.{nameof(rectangle.Height)}"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Effects |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// An <see langword="interface"/> used by the row delegates for a given <see cref="PixelRowDelegateProcessor{TPixel,TDelegate}"/> instance
|
||||
|
/// </summary>
|
||||
|
public interface IPixelRowDelegate |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Applies the current pixel row delegate to a target row of preprocessed pixels.
|
||||
|
/// </summary>
|
||||
|
/// <param name="span">The target row of <see cref="Vector4"/> pixels to process.</param>
|
||||
|
/// <param name="offset">The initial horizontal and vertical offset for the input pixels to process.</param>
|
||||
|
void Invoke(Span<Vector4> span, Point offset); |
||||
|
} |
||||
|
} |
||||
@ -1,70 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.Advanced.ParallelUtils; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Effects |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The base class for all processors that accept a user defined row processing delegate.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
internal abstract class PixelRowDelegateProcessorBase<TPixel> : ImageProcessor<TPixel> |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.
|
|
||||
/// </summary>
|
|
||||
private readonly PixelConversionModifiers modifiers; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="PixelRowDelegateProcessorBase{TPixel}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
|
|
||||
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
|
|
||||
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
|
|
||||
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
|
|
||||
protected PixelRowDelegateProcessorBase(Configuration configuration, PixelConversionModifiers modifiers, Image<TPixel> source, Rectangle sourceRectangle) |
|
||||
: base(configuration, source, sourceRectangle) |
|
||||
=> this.modifiers = modifiers; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void OnFrameApply(ImageFrame<TPixel> source) |
|
||||
{ |
|
||||
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); |
|
||||
int startX = interest.X; |
|
||||
Configuration configuration = this.Configuration; |
|
||||
PixelConversionModifiers modifiers = this.modifiers; |
|
||||
|
|
||||
ParallelHelper.IterateRowsWithTempBuffer<Vector4>( |
|
||||
interest, |
|
||||
this.Configuration, |
|
||||
(rows, vectorBuffer) => |
|
||||
{ |
|
||||
for (int y = rows.Min; y < rows.Max; y++) |
|
||||
{ |
|
||||
Span<Vector4> vectorSpan = vectorBuffer.Span; |
|
||||
int length = vectorSpan.Length; |
|
||||
Span<TPixel> rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); |
|
||||
PixelOperations<TPixel>.Instance.ToVector4(configuration, rowSpan, vectorSpan, modifiers); |
|
||||
|
|
||||
// Run the user defined pixel shader to the current row of pixels
|
|
||||
this.ApplyPixelRowDelegate(vectorSpan, new Point(startX, y)); |
|
||||
|
|
||||
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan, modifiers); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Applies the current pixel row delegate to a target row of preprocessed pixels.
|
|
||||
/// </summary>
|
|
||||
/// <param name="span">The target row of <see cref="Vector4"/> pixels to process.</param>
|
|
||||
/// <param name="offset">The initial horizontal and vertical offset for the input pixels to process.</param>
|
|
||||
protected abstract void ApplyPixelRowDelegate(Span<Vector4> span, Point offset); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,104 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Effects |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The base class for all processors that accept a user defined row processing delegate.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <typeparam name="TDelegate">The row processor type.</typeparam>
|
||||
|
internal sealed class PixelRowDelegateProcessor<TPixel, TDelegate> : ImageProcessor<TPixel> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
where TDelegate : struct, IPixelRowDelegate |
||||
|
{ |
||||
|
private readonly TDelegate rowDelegate; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.
|
||||
|
/// </summary>
|
||||
|
private readonly PixelConversionModifiers modifiers; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PixelRowDelegateProcessor{TPixel,TDelegate}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rowDelegate">The row processor to use to process each pixel row</param>
|
||||
|
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
|
||||
|
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
|
||||
|
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
|
||||
|
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
|
||||
|
public PixelRowDelegateProcessor( |
||||
|
in TDelegate rowDelegate, |
||||
|
Configuration configuration, |
||||
|
PixelConversionModifiers modifiers, |
||||
|
Image<TPixel> source, |
||||
|
Rectangle sourceRectangle) |
||||
|
: base(configuration, source, sourceRectangle) |
||||
|
{ |
||||
|
this.rowDelegate = rowDelegate; |
||||
|
this.modifiers = modifiers; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void OnFrameApply(ImageFrame<TPixel> source) |
||||
|
{ |
||||
|
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); |
||||
|
var operation = new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate); |
||||
|
|
||||
|
ParallelRowIterator.IterateRows<RowIntervalOperation, Vector4>( |
||||
|
this.Configuration, |
||||
|
interest, |
||||
|
in operation); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A <see langword="struct"/> implementing the convolution logic for <see cref="PixelRowDelegateProcessor{TPixel,TDelegate}"/>.
|
||||
|
/// </summary>
|
||||
|
private readonly struct RowIntervalOperation : IRowIntervalOperation<Vector4> |
||||
|
{ |
||||
|
private readonly int startX; |
||||
|
private readonly ImageFrame<TPixel> source; |
||||
|
private readonly Configuration configuration; |
||||
|
private readonly PixelConversionModifiers modifiers; |
||||
|
private readonly TDelegate rowProcessor; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowIntervalOperation( |
||||
|
int startX, |
||||
|
ImageFrame<TPixel> source, |
||||
|
Configuration configuration, |
||||
|
PixelConversionModifiers modifiers, |
||||
|
in TDelegate rowProcessor) |
||||
|
{ |
||||
|
this.startX = startX; |
||||
|
this.source = source; |
||||
|
this.configuration = configuration; |
||||
|
this.modifiers = modifiers; |
||||
|
this.rowProcessor = rowProcessor; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(in RowInterval rows, Span<Vector4> span) |
||||
|
{ |
||||
|
for (int y = rows.Min; y < rows.Max; y++) |
||||
|
{ |
||||
|
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); |
||||
|
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); |
||||
|
|
||||
|
// Run the user defined pixel shader to the current row of pixels
|
||||
|
Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y)); |
||||
|
|
||||
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,39 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Effects |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Applies a user defined row processing delegate to the image.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
internal sealed class PixelRowDelegateProcessor<TPixel> : PixelRowDelegateProcessorBase<TPixel> |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The user defined pixel row processing delegate.
|
|
||||
/// </summary>
|
|
||||
private readonly PixelRowOperation pixelRowOperation; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="PixelRowDelegateProcessor{TPixel}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
|
|
||||
/// <param name="definition">The <see cref="PixelRowDelegateProcessor"/> defining the processor parameters.</param>
|
|
||||
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
|
|
||||
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
|
|
||||
public PixelRowDelegateProcessor(Configuration configuration, PixelRowDelegateProcessor definition, Image<TPixel> source, Rectangle sourceRectangle) |
|
||||
: base(configuration, definition.Modifiers, source, sourceRectangle) |
|
||||
{ |
|
||||
this.pixelRowOperation = definition.PixelRowOperation; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ApplyPixelRowDelegate(Span<Vector4> span, Point offset) => this.pixelRowOperation(span); |
|
||||
} |
|
||||
} |
|
||||
@ -1,36 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Effects |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Applies a user defined, position aware, row processing delegate to the image.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
internal sealed class PositionAwarePixelRowDelegateProcessor<TPixel> : PixelRowDelegateProcessorBase<TPixel> |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
private readonly PixelRowOperation<Point> pixelRowOperation; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="PositionAwarePixelRowDelegateProcessor{TPixel}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
|
|
||||
/// <param name="definition">The <see cref="PositionAwarePixelRowDelegateProcessor"/> defining the processor parameters.</param>
|
|
||||
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
|
|
||||
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
|
|
||||
public PositionAwarePixelRowDelegateProcessor(Configuration configuration, PositionAwarePixelRowDelegateProcessor definition, Image<TPixel> source, Rectangle sourceRectangle) |
|
||||
: base(configuration, definition.Modifiers, source, sourceRectangle) |
|
||||
{ |
|
||||
this.pixelRowOperation = definition.PixelRowOperation; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void ApplyPixelRowDelegate(Span<Vector4> span, Point offset) => this.pixelRowOperation(span, offset); |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue