|
|
|
@ -3,7 +3,6 @@ |
|
|
|
|
|
|
|
using System.Buffers; |
|
|
|
using System.Numerics; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
@ -52,16 +51,53 @@ public abstract class PixelBlender<TPixel> |
|
|
|
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); |
|
|
|
|
|
|
|
using IMemoryOwner<Vector4> buffer = configuration.MemoryAllocator.Allocate<Vector4>(maxLength * 3); |
|
|
|
Span<Vector4> destinationVectors = buffer.Slice(0, maxLength); |
|
|
|
Span<Vector4> backgroundVectors = buffer.Slice(maxLength, maxLength); |
|
|
|
Span<Vector4> sourceVectors = buffer.Slice(maxLength * 2, maxLength); |
|
|
|
this.Blend( |
|
|
|
configuration, |
|
|
|
destination, |
|
|
|
background, |
|
|
|
source, |
|
|
|
amount, |
|
|
|
buffer.Memory.Span[..(maxLength * 3)]); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends 2 rows together using caller-provided temporary vector scratch.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
/// <param name="background">the background span</param>
|
|
|
|
/// <param name="source">the source span</param>
|
|
|
|
/// <param name="amount">
|
|
|
|
/// A value between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="workingBuffer">Reusable temporary vector scratch with capacity for at least 3 rows.</param>
|
|
|
|
public void Blend<TPixelSrc>( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
ReadOnlySpan<TPixelSrc> source, |
|
|
|
float amount, |
|
|
|
Span<Vector4> workingBuffer) |
|
|
|
where TPixelSrc : unmanaged, IPixel<TPixelSrc> |
|
|
|
{ |
|
|
|
int maxLength = destination.Length; |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); |
|
|
|
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); |
|
|
|
|
|
|
|
Span<Vector4> destinationVectors = workingBuffer[..maxLength]; |
|
|
|
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength); |
|
|
|
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); |
|
|
|
|
|
|
|
this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -87,14 +123,48 @@ public abstract class PixelBlender<TPixel> |
|
|
|
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); |
|
|
|
|
|
|
|
using IMemoryOwner<Vector4> buffer = configuration.MemoryAllocator.Allocate<Vector4>(maxLength * 2); |
|
|
|
Span<Vector4> destinationVectors = buffer.Slice(0, maxLength); |
|
|
|
Span<Vector4> backgroundVectors = buffer.Slice(maxLength, maxLength); |
|
|
|
this.Blend( |
|
|
|
configuration, |
|
|
|
destination, |
|
|
|
background, |
|
|
|
source, |
|
|
|
amount, |
|
|
|
buffer.Memory.Span[..(maxLength * 2)]); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends a row against a constant source color using caller-provided temporary vector scratch.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
/// <param name="background">the background span</param>
|
|
|
|
/// <param name="source">the source color</param>
|
|
|
|
/// <param name="amount">
|
|
|
|
/// A value between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="workingBuffer">Reusable temporary vector scratch with capacity for at least 2 rows.</param>
|
|
|
|
public void Blend( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
TPixel source, |
|
|
|
float amount, |
|
|
|
Span<Vector4> workingBuffer) |
|
|
|
{ |
|
|
|
int maxLength = destination.Length; |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); |
|
|
|
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); |
|
|
|
|
|
|
|
Span<Vector4> destinationVectors = workingBuffer[..maxLength]; |
|
|
|
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); |
|
|
|
|
|
|
|
this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -116,6 +186,27 @@ public abstract class PixelBlender<TPixel> |
|
|
|
ReadOnlySpan<float> amount) |
|
|
|
=> this.Blend<TPixel>(configuration, destination, background, source, amount); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends 2 rows together using caller-provided temporary vector scratch.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
/// <param name="background">the background span</param>
|
|
|
|
/// <param name="source">the source span</param>
|
|
|
|
/// <param name="amount">
|
|
|
|
/// A span with values between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="workingBuffer">Reusable temporary vector scratch with capacity for at least 3 rows.</param>
|
|
|
|
public void Blend( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
ReadOnlySpan<TPixel> source, |
|
|
|
ReadOnlySpan<float> amount, |
|
|
|
Span<Vector4> workingBuffer) |
|
|
|
=> this.Blend<TPixel>(configuration, destination, background, source, amount, workingBuffer); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends 2 rows together
|
|
|
|
/// </summary>
|
|
|
|
@ -142,20 +233,89 @@ public abstract class PixelBlender<TPixel> |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); |
|
|
|
|
|
|
|
using IMemoryOwner<Vector4> buffer = configuration.MemoryAllocator.Allocate<Vector4>(maxLength * 3); |
|
|
|
Span<Vector4> destinationVectors = buffer.Slice(0, maxLength); |
|
|
|
Span<Vector4> backgroundVectors = buffer.Slice(maxLength, maxLength); |
|
|
|
Span<Vector4> sourceVectors = buffer.Slice(maxLength * 2, maxLength); |
|
|
|
this.Blend( |
|
|
|
configuration, |
|
|
|
destination, |
|
|
|
background, |
|
|
|
source, |
|
|
|
amount, |
|
|
|
buffer.Memory.Span[..(maxLength * 3)]); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends a row against a constant source color.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
/// <param name="background">the background span</param>
|
|
|
|
/// <param name="source">the source color</param>
|
|
|
|
/// <param name="amount">
|
|
|
|
/// A span with values between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
public void Blend( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
TPixel source, |
|
|
|
ReadOnlySpan<float> amount) |
|
|
|
{ |
|
|
|
int maxLength = destination.Length; |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); |
|
|
|
|
|
|
|
using IMemoryOwner<Vector4> buffer = configuration.MemoryAllocator.Allocate<Vector4>(maxLength * 2); |
|
|
|
this.Blend( |
|
|
|
configuration, |
|
|
|
destination, |
|
|
|
background, |
|
|
|
source, |
|
|
|
amount, |
|
|
|
buffer.Memory.Span[..(maxLength * 2)]); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends 2 rows together using caller-provided temporary vector scratch.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
/// <param name="background">the background span</param>
|
|
|
|
/// <param name="source">the source span</param>
|
|
|
|
/// <param name="amount">
|
|
|
|
/// A span with values between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="workingBuffer">Reusable temporary vector scratch with capacity for at least 3 rows.</param>
|
|
|
|
public void Blend<TPixelSrc>( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
ReadOnlySpan<TPixelSrc> source, |
|
|
|
ReadOnlySpan<float> amount, |
|
|
|
Span<Vector4> workingBuffer) |
|
|
|
where TPixelSrc : unmanaged, IPixel<TPixelSrc> |
|
|
|
{ |
|
|
|
int maxLength = destination.Length; |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); |
|
|
|
|
|
|
|
Span<Vector4> destinationVectors = workingBuffer[..maxLength]; |
|
|
|
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength); |
|
|
|
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); |
|
|
|
|
|
|
|
this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Blends a row against a constant source color.
|
|
|
|
/// Blends a row against a constant source color using caller-provided temporary vector scratch.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
|
|
|
|
/// <param name="destination">the destination span</param>
|
|
|
|
@ -165,26 +325,28 @@ public abstract class PixelBlender<TPixel> |
|
|
|
/// A span with values between 0 and 1 indicating the weight of the second source vector.
|
|
|
|
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
|
|
|
|
/// </param>
|
|
|
|
/// <param name="workingBuffer">Reusable temporary vector scratch with capacity for at least 2 rows.</param>
|
|
|
|
public void Blend( |
|
|
|
Configuration configuration, |
|
|
|
Span<TPixel> destination, |
|
|
|
ReadOnlySpan<TPixel> background, |
|
|
|
TPixel source, |
|
|
|
ReadOnlySpan<float> amount) |
|
|
|
ReadOnlySpan<float> amount, |
|
|
|
Span<Vector4> workingBuffer) |
|
|
|
{ |
|
|
|
int maxLength = destination.Length; |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); |
|
|
|
Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); |
|
|
|
|
|
|
|
using IMemoryOwner<Vector4> buffer = configuration.MemoryAllocator.Allocate<Vector4>(maxLength * 2); |
|
|
|
Span<Vector4> destinationVectors = buffer.Slice(0, maxLength); |
|
|
|
Span<Vector4> backgroundVectors = buffer.Slice(maxLength, maxLength); |
|
|
|
Span<Vector4> destinationVectors = workingBuffer[..maxLength]; |
|
|
|
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); |
|
|
|
|
|
|
|
this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|