mirror of https://github.com/SixLabors/ImageSharp
768 changed files with 18808 additions and 14321 deletions
@ -1 +1 @@ |
|||||
Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5 |
Subproject commit ea561c249ba86352fe3b69e612b8072f3652eacb |
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
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,23 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
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,17 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the contract for an action that operates on a row.
|
||||
|
/// </summary>
|
||||
|
public interface IRowOperation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Invokes the method passing the row y coordinate.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The row y coordinate.</param>
|
||||
|
void Invoke(int y); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Advanced |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the contract for an action that operates on a row with a temporary buffer.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
|
||||
|
public interface IRowOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Invokes the method passing the row and a buffer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The row y coordinate.</param>
|
||||
|
/// <param name="span">The contiguous region of memory.</param>
|
||||
|
void Invoke(int y, Span<TBuffer> span); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// 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 RowOperationWrapper<T> |
||||
|
where T : struct, IRowOperation |
||||
|
{ |
||||
|
private readonly int minY; |
||||
|
private readonly int maxY; |
||||
|
private readonly int stepY; |
||||
|
private readonly T action; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowOperationWrapper( |
||||
|
int minY, |
||||
|
int maxY, |
||||
|
int stepY, |
||||
|
in T action) |
||||
|
{ |
||||
|
this.minY = minY; |
||||
|
this.maxY = maxY; |
||||
|
this.stepY = stepY; |
||||
|
this.action = action; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.minY + (i * this.stepY); |
||||
|
|
||||
|
if (yMin >= this.maxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.stepY, this.maxY); |
||||
|
|
||||
|
for (int y = yMin; y < yMax; y++) |
||||
|
{ |
||||
|
// Skip the safety copy when invoking a potentially impure method on a readonly field
|
||||
|
Unsafe.AsRef(this.action).Invoke(y); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly struct RowOperationWrapper<T, TBuffer> |
||||
|
where T : struct, IRowOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
private readonly int minY; |
||||
|
private readonly int maxY; |
||||
|
private readonly int stepY; |
||||
|
private readonly int width; |
||||
|
private readonly MemoryAllocator allocator; |
||||
|
private readonly T action; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowOperationWrapper( |
||||
|
int minY, |
||||
|
int maxY, |
||||
|
int stepY, |
||||
|
int width, |
||||
|
MemoryAllocator allocator, |
||||
|
in T action) |
||||
|
{ |
||||
|
this.minY = minY; |
||||
|
this.maxY = maxY; |
||||
|
this.stepY = stepY; |
||||
|
this.width = width; |
||||
|
this.allocator = allocator; |
||||
|
this.action = action; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.minY + (i * this.stepY); |
||||
|
|
||||
|
if (yMin >= this.maxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.stepY, this.maxY); |
||||
|
|
||||
|
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width); |
||||
|
|
||||
|
Span<TBuffer> span = buffer.Memory.Span; |
||||
|
|
||||
|
for (int y = yMin; y < yMax; y++) |
||||
|
{ |
||||
|
Unsafe.AsRef(this.action).Invoke(y, span); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly struct RowIntervalOperationWrapper<T> |
||||
|
where T : struct, IRowIntervalOperation |
||||
|
{ |
||||
|
private readonly int minY; |
||||
|
private readonly int maxY; |
||||
|
private readonly int stepY; |
||||
|
private readonly T operation; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowIntervalOperationWrapper( |
||||
|
int minY, |
||||
|
int maxY, |
||||
|
int stepY, |
||||
|
in T operation) |
||||
|
{ |
||||
|
this.minY = minY; |
||||
|
this.maxY = maxY; |
||||
|
this.stepY = stepY; |
||||
|
this.operation = operation; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.minY + (i * this.stepY); |
||||
|
|
||||
|
if (yMin >= this.maxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.stepY, this.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 int minY; |
||||
|
private readonly int maxY; |
||||
|
private readonly int stepY; |
||||
|
private readonly int width; |
||||
|
private readonly MemoryAllocator allocator; |
||||
|
private readonly T operation; |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public RowIntervalOperationWrapper( |
||||
|
int minY, |
||||
|
int maxY, |
||||
|
int stepY, |
||||
|
int width, |
||||
|
MemoryAllocator allocator, |
||||
|
in T operation) |
||||
|
{ |
||||
|
this.minY = minY; |
||||
|
this.maxY = maxY; |
||||
|
this.stepY = stepY; |
||||
|
this.width = width; |
||||
|
this.allocator = allocator; |
||||
|
this.operation = operation; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void Invoke(int i) |
||||
|
{ |
||||
|
int yMin = this.minY + (i * this.stepY); |
||||
|
|
||||
|
if (yMin >= this.maxY) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int yMax = Math.Min(yMin + this.stepY, this.maxY); |
||||
|
var rows = new RowInterval(yMin, yMax); |
||||
|
|
||||
|
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width); |
||||
|
|
||||
|
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,288 @@ |
|||||
|
// 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.
|
||||
|
/// </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 row.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowOperation |
||||
|
{ |
||||
|
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
||||
|
IterateRows(rectangle, in parallelSettings, in operation); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches.
|
||||
|
/// </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 row.</param>
|
||||
|
public static void IterateRows<T>( |
||||
|
Rectangle rectangle, |
||||
|
in ParallelExecutionSettings parallelSettings, |
||||
|
in T operation) |
||||
|
where T : struct, IRowOperation |
||||
|
{ |
||||
|
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) |
||||
|
{ |
||||
|
for (int y = top; y < bottom; y++) |
||||
|
{ |
||||
|
Unsafe.AsRef(operation).Invoke(y); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
||||
|
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
||||
|
var wrappingOperation = new RowOperationWrapper<T>(top, bottom, verticalStep, in operation); |
||||
|
|
||||
|
Parallel.For( |
||||
|
0, |
||||
|
numOfSteps, |
||||
|
parallelOptions, |
||||
|
wrappingOperation.Invoke); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Iterate through the rows of a rectangle in optimized batches.
|
||||
|
/// 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 row.</param>
|
||||
|
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowOperation<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.
|
||||
|
/// 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 row.</param>
|
||||
|
public static void IterateRows<T, TBuffer>( |
||||
|
Rectangle rectangle, |
||||
|
in ParallelExecutionSettings parallelSettings, |
||||
|
in T operation) |
||||
|
where T : struct, IRowOperation<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) |
||||
|
{ |
||||
|
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width); |
||||
|
Span<TBuffer> span = buffer.Memory.Span; |
||||
|
|
||||
|
for (int y = top; y < bottom; y++) |
||||
|
{ |
||||
|
Unsafe.AsRef(operation).Invoke(y, span); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int verticalStep = DivideCeil(height, numOfSteps); |
||||
|
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps }; |
||||
|
var wrappingOperation = new RowOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, 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.
|
||||
|
/// </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 IterateRowIntervals<T>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowIntervalOperation |
||||
|
{ |
||||
|
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
||||
|
IterateRowIntervals(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 IterateRowIntervals<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 wrappingOperation = new RowIntervalOperationWrapper<T>(top, bottom, verticalStep, 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 IterateRowIntervals<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation) |
||||
|
where T : struct, IRowIntervalOperation<TBuffer> |
||||
|
where TBuffer : unmanaged |
||||
|
{ |
||||
|
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); |
||||
|
IterateRowIntervals<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 IterateRowIntervals<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 wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, 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,160 +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) |
|
||||
{ |
|
||||
ParallelExecutionSettings 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 }; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
numOfSteps, |
|
||||
parallelOptions, |
|
||||
i => |
|
||||
{ |
|
||||
int yMin = rectangle.Top + (i * verticalStep); |
|
||||
int yMax = Math.Min(yMin + verticalStep, rectangle.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 }; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
numOfSteps, |
|
||||
parallelOptions, |
|
||||
i => |
|
||||
{ |
|
||||
int yMin = rectangle.Top + (i * verticalStep); |
|
||||
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,29 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp; |
||||
|
|
||||
|
namespace SixLabors |
||||
|
{ |
||||
|
internal static partial class Guard |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Ensures that the value is a value type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The target object, which cannot be null.</param>
|
||||
|
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
|
||||
|
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
|
/// <exception cref="ArgumentException"><paramref name="value"/> is not a value type.</exception>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void MustBeValueType<TValue>(TValue value, string parameterName) |
||||
|
{ |
||||
|
if (!value.GetType().GetTypeInfo().IsValueType) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp |
||||
|
{ |
||||
|
internal static partial class SimdUtils |
||||
|
{ |
||||
|
public static class Avx2Intrinsics |
||||
|
{ |
||||
|
private static ReadOnlySpan<byte> PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// <see cref="NormalizedFloatToByteSaturate"/> as many elements as possible, slicing them down (keeping the remainder).
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
internal static void NormalizedFloatToByteSaturateReduce( |
||||
|
ref ReadOnlySpan<float> source, |
||||
|
ref Span<byte> dest) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); |
||||
|
|
||||
|
if (Avx2.IsSupported) |
||||
|
{ |
||||
|
int remainder = ImageMaths.ModuloP2(source.Length, Vector<byte>.Count); |
||||
|
int adjustedCount = source.Length - remainder; |
||||
|
|
||||
|
if (adjustedCount > 0) |
||||
|
{ |
||||
|
NormalizedFloatToByteSaturate( |
||||
|
source.Slice(0, adjustedCount), |
||||
|
dest.Slice(0, adjustedCount)); |
||||
|
|
||||
|
source = source.Slice(adjustedCount); |
||||
|
dest = dest.Slice(adjustedCount); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implementation of <see cref="SimdUtils.NormalizedFloatToByteSaturate"/>, which is faster on new .NET runtime.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Implementation is based on MagicScaler code:
|
||||
|
/// https://github.com/saucecontrol/PhotoSauce/blob/a9bd6e5162d2160419f0cf743fd4f536c079170b/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L453-L477
|
||||
|
/// </remarks>
|
||||
|
internal static void NormalizedFloatToByteSaturate( |
||||
|
ReadOnlySpan<float> source, |
||||
|
Span<byte> dest) |
||||
|
{ |
||||
|
VerifySpanInput(source, dest, Vector256<byte>.Count); |
||||
|
|
||||
|
int n = dest.Length / Vector256<byte>.Count; |
||||
|
|
||||
|
ref Vector256<float> sourceBase = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
||||
|
ref Vector256<byte> destBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest)); |
||||
|
|
||||
|
var maxBytes = Vector256.Create(255f); |
||||
|
ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); |
||||
|
Vector256<int> mask = Unsafe.As<byte, Vector256<int>>(ref maskBase); |
||||
|
|
||||
|
for (int i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector256<float> s = ref Unsafe.Add(ref sourceBase, i * 4); |
||||
|
|
||||
|
Vector256<float> f0 = s; |
||||
|
Vector256<float> f1 = Unsafe.Add(ref s, 1); |
||||
|
Vector256<float> f2 = Unsafe.Add(ref s, 2); |
||||
|
Vector256<float> f3 = Unsafe.Add(ref s, 3); |
||||
|
|
||||
|
Vector256<int> w0 = ConvertToInt32(f0, maxBytes); |
||||
|
Vector256<int> w1 = ConvertToInt32(f1, maxBytes); |
||||
|
Vector256<int> w2 = ConvertToInt32(f2, maxBytes); |
||||
|
Vector256<int> w3 = ConvertToInt32(f3, maxBytes); |
||||
|
|
||||
|
Vector256<short> u0 = Avx2.PackSignedSaturate(w0, w1); |
||||
|
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3); |
||||
|
Vector256<byte> b = Avx2.PackUnsignedSaturate(u0, u1); |
||||
|
b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); |
||||
|
|
||||
|
Unsafe.Add(ref destBase, i) = b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<int> ConvertToInt32(Vector256<float> vf, Vector256<float> scale) |
||||
|
{ |
||||
|
vf = Avx.Multiply(vf, scale); |
||||
|
return Avx.ConvertToVector256Int32(vf); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -1,112 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tuples |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains 8 element value tuples of various types.
|
|
||||
/// </summary>
|
|
||||
internal static class Octet |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Value tuple of <see cref="uint"/>-s.
|
|
||||
/// </summary>
|
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] |
|
||||
public struct OfUInt32 |
|
||||
{ |
|
||||
[FieldOffset(0 * sizeof(uint))] |
|
||||
public uint V0; |
|
||||
|
|
||||
[FieldOffset(1 * sizeof(uint))] |
|
||||
public uint V1; |
|
||||
|
|
||||
[FieldOffset(2 * sizeof(uint))] |
|
||||
public uint V2; |
|
||||
|
|
||||
[FieldOffset(3 * sizeof(uint))] |
|
||||
public uint V3; |
|
||||
|
|
||||
[FieldOffset(4 * sizeof(uint))] |
|
||||
public uint V4; |
|
||||
|
|
||||
[FieldOffset(5 * sizeof(uint))] |
|
||||
public uint V5; |
|
||||
|
|
||||
[FieldOffset(6 * sizeof(uint))] |
|
||||
public uint V6; |
|
||||
|
|
||||
[FieldOffset(7 * sizeof(uint))] |
|
||||
public uint V7; |
|
||||
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"{nameof(Octet)}.{nameof(OfUInt32)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void LoadFrom(ref OfByte src) |
|
||||
{ |
|
||||
this.V0 = src.V0; |
|
||||
this.V1 = src.V1; |
|
||||
this.V2 = src.V2; |
|
||||
this.V3 = src.V3; |
|
||||
this.V4 = src.V4; |
|
||||
this.V5 = src.V5; |
|
||||
this.V6 = src.V6; |
|
||||
this.V7 = src.V7; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Value tuple of <see cref="byte"/>-s
|
|
||||
/// </summary>
|
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8)] |
|
||||
public struct OfByte |
|
||||
{ |
|
||||
[FieldOffset(0)] |
|
||||
public byte V0; |
|
||||
|
|
||||
[FieldOffset(1)] |
|
||||
public byte V1; |
|
||||
|
|
||||
[FieldOffset(2)] |
|
||||
public byte V2; |
|
||||
|
|
||||
[FieldOffset(3)] |
|
||||
public byte V3; |
|
||||
|
|
||||
[FieldOffset(4)] |
|
||||
public byte V4; |
|
||||
|
|
||||
[FieldOffset(5)] |
|
||||
public byte V5; |
|
||||
|
|
||||
[FieldOffset(6)] |
|
||||
public byte V6; |
|
||||
|
|
||||
[FieldOffset(7)] |
|
||||
public byte V7; |
|
||||
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"{nameof(Octet)}.{nameof(OfByte)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void LoadFrom(ref OfUInt32 src) |
|
||||
{ |
|
||||
this.V0 = (byte)src.V0; |
|
||||
this.V1 = (byte)src.V1; |
|
||||
this.V2 = (byte)src.V2; |
|
||||
this.V3 = (byte)src.V3; |
|
||||
this.V4 = (byte)src.V4; |
|
||||
this.V5 = (byte)src.V5; |
|
||||
this.V6 = (byte)src.V6; |
|
||||
this.V7 = (byte)src.V7; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,73 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tuples |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains 8 element value tuples of various types.
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal struct Octet<T> |
||||
|
where T : unmanaged |
||||
|
{ |
||||
|
public T V0; |
||||
|
public T V1; |
||||
|
public T V2; |
||||
|
public T V3; |
||||
|
public T V4; |
||||
|
public T V5; |
||||
|
public T V6; |
||||
|
public T V7; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override readonly string ToString() |
||||
|
{ |
||||
|
return $"Octet<{typeof(T)}>({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for the <see cref="Octet{T}"/> type.
|
||||
|
/// </summary>
|
||||
|
internal static class OctetExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Loads the fields in a target <see cref="Octet{T}"/> of <see cref="uint"/> from one of <see cref="byte"/> type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="destination">The target <see cref="Octet{T}"/> of <see cref="uint"/> instance.</param>
|
||||
|
/// <param name="source">The source <see cref="Octet{T}"/> of <see cref="byte"/> instance.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void LoadFrom(ref this Octet<uint> destination, ref Octet<byte> source) |
||||
|
{ |
||||
|
destination.V0 = source.V0; |
||||
|
destination.V1 = source.V1; |
||||
|
destination.V2 = source.V2; |
||||
|
destination.V3 = source.V3; |
||||
|
destination.V4 = source.V4; |
||||
|
destination.V5 = source.V5; |
||||
|
destination.V6 = source.V6; |
||||
|
destination.V7 = source.V7; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads the fields in a target <see cref="Octet{T}"/> of <see cref="byte"/> from one of <see cref="uint"/> type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="destination">The target <see cref="Octet{T}"/> of <see cref="byte"/> instance.</param>
|
||||
|
/// <param name="source">The source <see cref="Octet{T}"/> of <see cref="uint"/> instance.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void LoadFrom(ref this Octet<byte> destination, ref Octet<uint> source) |
||||
|
{ |
||||
|
destination.V0 = (byte)source.V0; |
||||
|
destination.V1 = (byte)source.V1; |
||||
|
destination.V2 = (byte)source.V2; |
||||
|
destination.V3 = (byte)source.V3; |
||||
|
destination.V4 = (byte)source.V4; |
||||
|
destination.V5 = (byte)source.V5; |
||||
|
destination.V6 = (byte)source.V6; |
||||
|
destination.V7 = (byte)source.V7; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>.
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal readonly ref struct RowOctet<T> |
||||
|
where T : struct |
||||
|
{ |
||||
|
private readonly Span<T> row0; |
||||
|
private readonly Span<T> row1; |
||||
|
private readonly Span<T> row2; |
||||
|
private readonly Span<T> row3; |
||||
|
private readonly Span<T> row4; |
||||
|
private readonly Span<T> row5; |
||||
|
private readonly Span<T> row6; |
||||
|
private readonly Span<T> row7; |
||||
|
|
||||
|
public RowOctet(Buffer2D<T> buffer, int startY) |
||||
|
{ |
||||
|
int y = startY; |
||||
|
int height = buffer.Height; |
||||
|
this.row0 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row1 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row2 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row3 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row4 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row5 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row6 = y < height ? buffer.GetRowSpan(y++) : default; |
||||
|
this.row7 = y < height ? buffer.GetRowSpan(y) : default; |
||||
|
} |
||||
|
|
||||
|
public Span<T> this[int y] |
||||
|
{ |
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
get |
||||
|
{ |
||||
|
// No unsafe tricks, since Span<T> can't be used as a generic argument
|
||||
|
return y switch |
||||
|
{ |
||||
|
0 => this.row0, |
||||
|
1 => this.row1, |
||||
|
2 => this.row2, |
||||
|
3 => this.row3, |
||||
|
4 => this.row4, |
||||
|
5 => this.row5, |
||||
|
6 => this.row6, |
||||
|
7 => this.row7, |
||||
|
_ => ThrowIndexOutOfRangeException() |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ColdPath)] |
||||
|
private static Span<T> ThrowIndexOutOfRangeException() |
||||
|
{ |
||||
|
throw new IndexOutOfRangeException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue