mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
235 changed files with 106499 additions and 1296 deletions
@ -0,0 +1,387 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Provides bulk operations for pixel formats that store associated alpha.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The associated-alpha pixel format.</typeparam>
|
|||
public abstract class AssociatedAlphaPixelOperations<TPixel> : PixelOperations<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <inheritdoc />
|
|||
public override PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) |
|||
=> AssociatedAlphaPixelBlenders<TPixel>.GetPixelBlender(colorMode, alphaMode); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void ToUnassociatedVector4( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<Vector4> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void ToAssociatedVector4( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<Vector4> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void FromUnassociatedVector4Destructive( |
|||
Configuration configuration, |
|||
Span<Vector4> source, |
|||
Span<TPixel> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void FromAssociatedVector4Destructive( |
|||
Configuration configuration, |
|||
Span<Vector4> source, |
|||
Span<TPixel> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void ToUnassociatedScaledVector4( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<Vector4> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void ToAssociatedScaledVector4( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<Vector4> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void FromUnassociatedScaledVector4Destructive( |
|||
Configuration configuration, |
|||
Span<Vector4> source, |
|||
Span<TPixel> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected abstract override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<TPixel> destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void From<TSourcePixel>( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TSourcePixel> source, |
|||
Span<TPixel> destination) |
|||
{ |
|||
if (source.IsEmpty) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// Cap large conversions at 1,024 vectors while avoiding a 16 KiB rental for short spans.
|
|||
int sliceLength = Math.Min(source.Length, 1024); |
|||
int numberOfSlices = source.Length / sliceLength; |
|||
|
|||
using IMemoryOwner<Vector4> tempVectors = configuration.MemoryAllocator.Allocate<Vector4>(sliceLength); |
|||
Span<Vector4> vectorSpan = tempVectors.GetSpan()[..sliceLength]; |
|||
|
|||
// Convert through unassociated vectors so the destination operation can quantize alpha to its own storage before associating RGB.
|
|||
for (int i = 0; i < numberOfSlices; i++) |
|||
{ |
|||
int start = i * sliceLength; |
|||
ReadOnlySpan<TSourcePixel> sourceSlice = source.Slice(start, sliceLength); |
|||
Span<TPixel> destinationSlice = destination.Slice(start, sliceLength); |
|||
PixelOperations<TSourcePixel>.Instance.ToVector4( |
|||
configuration, |
|||
sourceSlice, |
|||
vectorSpan, |
|||
PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); |
|||
|
|||
this.FromUnassociatedScaledVector4Destructive(configuration, vectorSpan, destinationSlice); |
|||
} |
|||
|
|||
int endOfCompleteSlices = numberOfSlices * sliceLength; |
|||
int remainder = source.Length - endOfCompleteSlices; |
|||
|
|||
if (remainder > 0) |
|||
{ |
|||
ReadOnlySpan<TSourcePixel> sourceSlice = source[endOfCompleteSlices..]; |
|||
Span<TPixel> destinationSlice = destination.Slice(endOfCompleteSlices, remainder); |
|||
vectorSpan = vectorSpan[..remainder]; |
|||
PixelOperations<TSourcePixel>.Instance.ToVector4( |
|||
configuration, |
|||
sourceSlice, |
|||
vectorSpan, |
|||
PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); |
|||
|
|||
this.FromUnassociatedScaledVector4Destructive(configuration, vectorSpan, destinationSlice); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromVector4Destructive( |
|||
Configuration configuration, |
|||
Span<Vector4> sourceVectors, |
|||
Span<TPixel> destination, |
|||
PixelConversionModifiers modifiers) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); |
|||
|
|||
bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply) || !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply); |
|||
bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); |
|||
|
|||
if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) |
|||
{ |
|||
// Transfer functions operate on straight color components. Associated input must therefore be unassociated before companding.
|
|||
if (associated) |
|||
{ |
|||
Numerics.UnPremultiply(sourceVectors); |
|||
} |
|||
|
|||
SRgbCompanding.Compress(sourceVectors); |
|||
|
|||
if (scaled) |
|||
{ |
|||
this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
else |
|||
{ |
|||
this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (scaled) |
|||
{ |
|||
if (associated) |
|||
{ |
|||
this.FromAssociatedScaledVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
else |
|||
{ |
|||
this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
} |
|||
else if (associated) |
|||
{ |
|||
this.FromAssociatedVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
else |
|||
{ |
|||
this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToVector4( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<Vector4> destinationVectors, |
|||
PixelConversionModifiers modifiers) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
Guard.DestinationShouldNotBeTooShort(source, destinationVectors, nameof(destinationVectors)); |
|||
|
|||
bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply) || !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply); |
|||
bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); |
|||
|
|||
if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) |
|||
{ |
|||
// Extract straight color before applying the transfer function; companding associated components would make RGB depend on alpha.
|
|||
if (scaled) |
|||
{ |
|||
this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); |
|||
} |
|||
else |
|||
{ |
|||
this.ToUnassociatedVector4(configuration, source, destinationVectors); |
|||
} |
|||
|
|||
Span<Vector4> converted = destinationVectors[..source.Length]; |
|||
SRgbCompanding.Expand(converted); |
|||
|
|||
if (associated) |
|||
{ |
|||
Numerics.Premultiply(converted); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (scaled) |
|||
{ |
|||
if (associated) |
|||
{ |
|||
this.ToAssociatedScaledVector4(configuration, source, destinationVectors); |
|||
} |
|||
else |
|||
{ |
|||
this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); |
|||
} |
|||
} |
|||
else if (associated) |
|||
{ |
|||
this.ToAssociatedVector4(configuration, source, destinationVectors); |
|||
} |
|||
else |
|||
{ |
|||
this.ToUnassociatedVector4(configuration, source, destinationVectors); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromArgb32(Configuration configuration, ReadOnlySpan<Argb32> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromAbgr32(Configuration configuration, ReadOnlySpan<Abgr32> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromBgr24(Configuration configuration, ReadOnlySpan<Bgr24> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromBgra32(Configuration configuration, ReadOnlySpan<Bgra32> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromL8(Configuration configuration, ReadOnlySpan<L8> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromL16(Configuration configuration, ReadOnlySpan<L16> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromLa16(Configuration configuration, ReadOnlySpan<La16> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromLa32(Configuration configuration, ReadOnlySpan<La32> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromRgb24(Configuration configuration, ReadOnlySpan<Rgb24> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromRgba32(Configuration configuration, ReadOnlySpan<Rgba32> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromRgb48(Configuration configuration, ReadOnlySpan<Rgb48> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromRgba64(Configuration configuration, ReadOnlySpan<Rgba64> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void FromBgra5551(Configuration configuration, ReadOnlySpan<Bgra5551> source, Span<TPixel> destination) |
|||
=> this.From(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToArgb32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Argb32> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToAbgr32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Abgr32> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToBgr24(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgr24> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToBgra32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgra32> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToL8(Configuration configuration, ReadOnlySpan<TPixel> source, Span<L8> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToL16(Configuration configuration, ReadOnlySpan<TPixel> source, Span<L16> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToLa16(Configuration configuration, ReadOnlySpan<TPixel> source, Span<La16> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToLa32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<La32> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToRgb24(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgb24> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToRgba32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgba32> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToRgb48(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgb48> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToRgba64(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgba64> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
public override void ToBgra5551(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgra5551> destination) |
|||
=> this.ConvertToUnassociated(configuration, source, destination); |
|||
|
|||
/// <summary>
|
|||
/// Converts associated source pixels to an unassociated destination format.
|
|||
/// </summary>
|
|||
/// <typeparam name="TDestinationPixel">The destination pixel format.</typeparam>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
/// <param name="source">The source pixels.</param>
|
|||
/// <param name="destination">The destination pixels.</param>
|
|||
private void ConvertToUnassociated<TDestinationPixel>( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixel> source, |
|||
Span<TDestinationPixel> destination) |
|||
where TDestinationPixel : unmanaged, IPixel<TDestinationPixel> |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
|||
|
|||
if (source.IsEmpty) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
int sliceLength = Math.Min(source.Length, 1024); |
|||
int numberOfSlices = source.Length / sliceLength; |
|||
|
|||
using IMemoryOwner<Vector4> tempVectors = configuration.MemoryAllocator.Allocate<Vector4>(sliceLength); |
|||
Span<Vector4> vectorSpan = tempVectors.GetSpan()[..sliceLength]; |
|||
PixelOperations<TDestinationPixel> destinationOperations = PixelOperations<TDestinationPixel>.Instance; |
|||
|
|||
// Generated destination dispatch routes conversion back through this source operation's ToX override. Extract through this
|
|||
// operation's protected bulk hook, then use the destination's public modifier contract to avoid recursive dispatch.
|
|||
for (int i = 0; i < numberOfSlices; i++) |
|||
{ |
|||
int start = i * sliceLength; |
|||
ReadOnlySpan<TPixel> sourceSlice = source.Slice(start, sliceLength); |
|||
Span<TDestinationPixel> destinationSlice = destination.Slice(start, sliceLength); |
|||
this.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); |
|||
destinationOperations.FromVector4Destructive(configuration, vectorSpan, destinationSlice, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); |
|||
} |
|||
|
|||
int endOfCompleteSlices = numberOfSlices * sliceLength; |
|||
int remainder = source.Length - endOfCompleteSlices; |
|||
|
|||
if (remainder > 0) |
|||
{ |
|||
ReadOnlySpan<TPixel> sourceSlice = source[endOfCompleteSlices..]; |
|||
Span<TDestinationPixel> destinationSlice = destination.Slice(endOfCompleteSlices, remainder); |
|||
vectorSpan = vectorSpan[..remainder]; |
|||
this.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); |
|||
destinationOperations.FromVector4Destructive(configuration, vectorSpan, destinationSlice, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,847 @@ |
|||
<# |
|||
// Copyright (c) Six Labors. |
|||
// Licensed under the Six Labors Split License. |
|||
#> |
|||
<#@ template debug="false" hostspecific="false" language="C#" #> |
|||
<#@ assembly name="System.Core" #> |
|||
<#@ import namespace="System.Linq" #> |
|||
<#@ import namespace="System.Text" #> |
|||
<#@ import namespace="System.Collections.Generic" #> |
|||
<#@ output extension=".cs" #> |
|||
// Copyright (c) Six Labors. |
|||
// Licensed under the Six Labors Split License. |
|||
|
|||
// <auto-generated /> |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
/// <summary> |
|||
/// Provides generated Porter-Duff blenders for associated-alpha pixel formats. |
|||
/// </summary> |
|||
internal static partial class AssociatedAlphaPixelBlenders<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
|
|||
<# |
|||
var composers = new []{ |
|||
"Src", |
|||
"SrcAtop", |
|||
"SrcOver", |
|||
"SrcIn", |
|||
"SrcOut", |
|||
"Dest", |
|||
"DestAtop", |
|||
"DestOver", |
|||
"DestIn", |
|||
"DestOut", |
|||
"Clear", |
|||
"Xor", |
|||
}; |
|||
|
|||
var blenders = new []{ |
|||
"Normal", |
|||
"Multiply", |
|||
"Add", |
|||
"Subtract", |
|||
"Screen", |
|||
"Darken", |
|||
"Lighten", |
|||
"Overlay", |
|||
"HardLight" |
|||
}; |
|||
|
|||
foreach(var composer in composers) { |
|||
foreach(var blender in blenders) { |
|||
|
|||
var blender_composer= $"{blender}{composer}"; |
|||
#> |
|||
/// <summary> |
|||
/// A pixel blender that implements the "<#= blender_composer#>" composition equation. |
|||
/// </summary> |
|||
public sealed class <#= blender_composer#> : AssociatedAlphaPixelBlender<TPixel> |
|||
{ |
|||
/// <summary> |
|||
/// Gets the static instance of this blender. |
|||
/// </summary> |
|||
public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); |
|||
|
|||
/// <inheritdoc /> |
|||
public override TPixel Blend(TPixel background, TPixel source, float amount) |
|||
{ |
|||
return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1))); |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount) |
|||
{ |
|||
amount = Numerics.Clamp(amount, 0, 1); |
|||
|
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source)); |
|||
Vector512<float> opacity = Vector512.Create(amount); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
Vector256<float> opacity = Vector256.Create(amount); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, float amount) |
|||
{ |
|||
amount = Numerics.Clamp(amount, 0, 1); |
|||
|
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
Vector512<float> sourceBase = Vector512.Create( |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W); |
|||
Vector512<float> opacity = Vector512.Create(amount); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); |
|||
Vector256<float> opacity = Vector256.Create(amount); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount) |
|||
{ |
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
|
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float amount0 = amountBase; |
|||
float amount1 = Unsafe.Add(ref amountBase, 1); |
|||
float amount2 = Unsafe.Add(ref amountBase, 2); |
|||
float amount3 = Unsafe.Add(ref amountBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four amount values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> opacity = Vector512.Create( |
|||
amount0, amount0, amount0, amount0, |
|||
amount1, amount1, amount1, amount1, |
|||
amount2, amount2, amount2, amount2, |
|||
amount3, amount3, amount3, amount3); |
|||
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne); |
|||
|
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
|
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next amount values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> opacity = Vector256.Create( |
|||
Vector128.Create(amountBase), |
|||
Vector128.Create(Unsafe.Add(ref amountBase, 1))); |
|||
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne); |
|||
|
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, ReadOnlySpan<float> amount) |
|||
{ |
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
|
|||
Vector512<float> sourceBase = Vector512.Create( |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W); |
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float amount0 = amountBase; |
|||
float amount1 = Unsafe.Add(ref amountBase, 1); |
|||
float amount2 = Unsafe.Add(ref amountBase, 2); |
|||
float amount3 = Unsafe.Add(ref amountBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four amount values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> opacity = Vector512.Create( |
|||
amount0, amount0, amount0, amount0, |
|||
amount1, amount1, amount1, amount1, |
|||
amount2, amount2, amount2, amount2, |
|||
amount3, amount3, amount3, amount3); |
|||
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne); |
|||
|
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
|
|||
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); |
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next amount values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> opacity = Vector256.Create( |
|||
Vector128.Create(amountBase), |
|||
Vector128.Create(Unsafe.Add(ref amountBase, 1))); |
|||
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne); |
|||
|
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount, ReadOnlySpan<float> coverage) |
|||
{ |
|||
amount = Numerics.Clamp(amount, 0, 1); |
|||
|
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
Vector512<float> opacity = Vector512.Create(amount); |
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float coverage0 = coverageBase; |
|||
float coverage1 = Unsafe.Add(ref coverageBase, 1); |
|||
float coverage2 = Unsafe.Add(ref coverageBase, 2); |
|||
float coverage3 = Unsafe.Add(ref coverageBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four coverage values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> coverageVector = Vector512.Create( |
|||
coverage0, coverage0, coverage0, coverage0, |
|||
coverage1, coverage1, coverage1, coverage1, |
|||
coverage2, coverage2, coverage2, coverage2, |
|||
coverage3, coverage3, coverage3, coverage3); |
|||
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
Vector256<float> opacity = Vector256.Create(amount); |
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next coverage values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> coverageVector = Vector256.Create( |
|||
Vector128.Create(coverageBase), |
|||
Vector128.Create(Unsafe.Add(ref coverageBase, 1))); |
|||
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, float amount, ReadOnlySpan<float> coverage) |
|||
{ |
|||
amount = Numerics.Clamp(amount, 0, 1); |
|||
|
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector512<float> sourceBase = Vector512.Create( |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W); |
|||
Vector512<float> opacity = Vector512.Create(amount); |
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float coverage0 = coverageBase; |
|||
float coverage1 = Unsafe.Add(ref coverageBase, 1); |
|||
float coverage2 = Unsafe.Add(ref coverageBase, 2); |
|||
float coverage3 = Unsafe.Add(ref coverageBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four coverage values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> coverageVector = Vector512.Create( |
|||
coverage0, coverage0, coverage0, coverage0, |
|||
coverage1, coverage1, coverage1, coverage1, |
|||
coverage2, coverage2, coverage2, coverage2, |
|||
coverage3, coverage3, coverage3, coverage3); |
|||
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); |
|||
Vector256<float> opacity = Vector256.Create(amount); |
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next coverage values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> coverageVector = Vector256.Create( |
|||
Vector128.Create(coverageBase), |
|||
Vector128.Create(Unsafe.Add(ref coverageBase, 1))); |
|||
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount, ReadOnlySpan<float> coverage) |
|||
{ |
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float amount0 = amountBase; |
|||
float amount1 = Unsafe.Add(ref amountBase, 1); |
|||
float amount2 = Unsafe.Add(ref amountBase, 2); |
|||
float amount3 = Unsafe.Add(ref amountBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four amount values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> opacity = Vector512.Create( |
|||
amount0, amount0, amount0, amount0, |
|||
amount1, amount1, amount1, amount1, |
|||
amount2, amount2, amount2, amount2, |
|||
amount3, amount3, amount3, amount3); |
|||
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne); |
|||
|
|||
float coverage0 = coverageBase; |
|||
float coverage1 = Unsafe.Add(ref coverageBase, 1); |
|||
float coverage2 = Unsafe.Add(ref coverageBase, 2); |
|||
float coverage3 = Unsafe.Add(ref coverageBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four coverage values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> coverageVector = Vector512.Create( |
|||
coverage0, coverage0, coverage0, coverage0, |
|||
coverage1, coverage1, coverage1, coverage1, |
|||
coverage2, coverage2, coverage2, coverage2, |
|||
coverage3, coverage3, coverage3, coverage3); |
|||
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 4); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next amount values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> opacity = Vector256.Create( |
|||
Vector128.Create(amountBase), |
|||
Vector128.Create(Unsafe.Add(ref amountBase, 1))); |
|||
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne); |
|||
|
|||
// We need to create a Vector256<float> containing the current and next coverage values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> coverageVector = Vector256.Create( |
|||
Vector128.Create(coverageBase), |
|||
Vector128.Create(Unsafe.Add(ref coverageBase, 1))); |
|||
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
sourceBase = ref Unsafe.Add(ref sourceBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 2); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc /> |
|||
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, ReadOnlySpan<float> amount, ReadOnlySpan<float> coverage) |
|||
{ |
|||
if (Avx512F.IsSupported && destination.Length >= 4) |
|||
{ |
|||
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float> |
|||
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u); |
|||
|
|||
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector512<float> sourceBase = Vector512.Create( |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W, |
|||
source.X, source.Y, source.Z, source.W); |
|||
Vector512<float> vOne = Vector512.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
float amount0 = amountBase; |
|||
float amount1 = Unsafe.Add(ref amountBase, 1); |
|||
float amount2 = Unsafe.Add(ref amountBase, 2); |
|||
float amount3 = Unsafe.Add(ref amountBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four amount values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> opacity = Vector512.Create( |
|||
amount0, amount0, amount0, amount0, |
|||
amount1, amount1, amount1, amount1, |
|||
amount2, amount2, amount2, amount2, |
|||
amount3, amount3, amount3, amount3); |
|||
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne); |
|||
|
|||
float coverage0 = coverageBase; |
|||
float coverage1 = Unsafe.Add(ref coverageBase, 1); |
|||
float coverage2 = Unsafe.Add(ref coverageBase, 2); |
|||
float coverage3 = Unsafe.Add(ref coverageBase, 3); |
|||
|
|||
// We need to create a Vector512<float> containing the current four coverage values |
|||
// taking up each quarter of the Vector512<float> and then clamp them. |
|||
Vector512<float> coverageVector = Vector512.Create( |
|||
coverage0, coverage0, coverage0, coverage0, |
|||
coverage1, coverage1, coverage1, coverage1, |
|||
coverage2, coverage2, coverage2, coverage2, |
|||
coverage3, coverage3, coverage3, coverage3); |
|||
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 4); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 4); |
|||
} |
|||
|
|||
int remainder = Numerics.Modulo4(destination.Length); |
|||
if (remainder != 0) |
|||
{ |
|||
for (int i = destination.Length - remainder; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
else if (Avx2.IsSupported && destination.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> |
|||
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination)); |
|||
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); |
|||
|
|||
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background)); |
|||
ref float amountBase = ref MemoryMarshal.GetReference(amount); |
|||
ref float coverageBase = ref MemoryMarshal.GetReference(coverage); |
|||
|
|||
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); |
|||
Vector256<float> vOne = Vector256.Create(1F); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) |
|||
{ |
|||
// We need to create a Vector256<float> containing the current and next amount values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> opacity = Vector256.Create( |
|||
Vector128.Create(amountBase), |
|||
Vector128.Create(Unsafe.Add(ref amountBase, 1))); |
|||
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne); |
|||
|
|||
// We need to create a Vector256<float> containing the current and next coverage values |
|||
// taking up each half of the Vector256<float> and then clamp them. |
|||
Vector256<float> coverageVector = Vector256.Create( |
|||
Vector128.Create(coverageBase), |
|||
Vector128.Create(Unsafe.Add(ref coverageBase, 1))); |
|||
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne); |
|||
|
|||
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); |
|||
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector); |
|||
destinationBase = ref Unsafe.Add(ref destinationBase, 1); |
|||
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); |
|||
amountBase = ref Unsafe.Add(ref amountBase, 2); |
|||
coverageBase = ref Unsafe.Add(ref coverageBase, 2); |
|||
} |
|||
|
|||
if (Numerics.Modulo2(destination.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. |
|||
int i = destination.Length - 1; |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F)); |
|||
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
<# |
|||
} |
|||
} |
|||
|
|||
#> |
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
/// <summary>
|
|||
/// Provides pixel blenders for formats that store associated alpha.
|
|||
/// </summary>
|
|||
internal static partial class AssociatedAlphaPixelBlenders<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the blender for the requested color blending and alpha composition modes.
|
|||
/// </summary>
|
|||
/// <param name="colorMode">The color blending mode.</param>
|
|||
/// <param name="alphaMode">The alpha composition mode.</param>
|
|||
/// <returns>The pixel blender.</returns>
|
|||
public static PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) |
|||
{ |
|||
return alphaMode switch |
|||
{ |
|||
PixelAlphaCompositionMode.Src => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplySrc.Instance, |
|||
PixelColorBlendingMode.Add => AddSrc.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractSrc.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenSrc.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenSrc.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenSrc.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlaySrc.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightSrc.Instance, |
|||
_ => NormalSrc.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.SrcAtop => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplySrcAtop.Instance, |
|||
PixelColorBlendingMode.Add => AddSrcAtop.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractSrcAtop.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenSrcAtop.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenSrcAtop.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenSrcAtop.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlaySrcAtop.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightSrcAtop.Instance, |
|||
_ => NormalSrcAtop.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.SrcIn => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplySrcIn.Instance, |
|||
PixelColorBlendingMode.Add => AddSrcIn.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractSrcIn.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenSrcIn.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenSrcIn.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenSrcIn.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlaySrcIn.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightSrcIn.Instance, |
|||
_ => NormalSrcIn.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.SrcOut => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplySrcOut.Instance, |
|||
PixelColorBlendingMode.Add => AddSrcOut.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractSrcOut.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenSrcOut.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenSrcOut.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenSrcOut.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlaySrcOut.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightSrcOut.Instance, |
|||
_ => NormalSrcOut.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.Dest => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyDest.Instance, |
|||
PixelColorBlendingMode.Add => AddDest.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractDest.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenDest.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenDest.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenDest.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayDest.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightDest.Instance, |
|||
_ => NormalDest.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.DestAtop => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyDestAtop.Instance, |
|||
PixelColorBlendingMode.Add => AddDestAtop.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractDestAtop.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenDestAtop.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenDestAtop.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenDestAtop.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayDestAtop.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightDestAtop.Instance, |
|||
_ => NormalDestAtop.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.DestOver => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyDestOver.Instance, |
|||
PixelColorBlendingMode.Add => AddDestOver.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractDestOver.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenDestOver.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenDestOver.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenDestOver.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayDestOver.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightDestOver.Instance, |
|||
_ => NormalDestOver.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.DestIn => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyDestIn.Instance, |
|||
PixelColorBlendingMode.Add => AddDestIn.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractDestIn.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenDestIn.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenDestIn.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenDestIn.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayDestIn.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightDestIn.Instance, |
|||
_ => NormalDestIn.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.DestOut => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyDestOut.Instance, |
|||
PixelColorBlendingMode.Add => AddDestOut.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractDestOut.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenDestOut.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenDestOut.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenDestOut.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayDestOut.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightDestOut.Instance, |
|||
_ => NormalDestOut.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.Clear => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyClear.Instance, |
|||
PixelColorBlendingMode.Add => AddClear.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractClear.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenClear.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenClear.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenClear.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayClear.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightClear.Instance, |
|||
_ => NormalClear.Instance, |
|||
}, |
|||
PixelAlphaCompositionMode.Xor => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplyXor.Instance, |
|||
PixelColorBlendingMode.Add => AddXor.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractXor.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenXor.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenXor.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenXor.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlayXor.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightXor.Instance, |
|||
_ => NormalXor.Instance, |
|||
}, |
|||
_ => colorMode switch |
|||
{ |
|||
PixelColorBlendingMode.Multiply => MultiplySrcOver.Instance, |
|||
PixelColorBlendingMode.Add => AddSrcOver.Instance, |
|||
PixelColorBlendingMode.Subtract => SubtractSrcOver.Instance, |
|||
PixelColorBlendingMode.Screen => ScreenSrcOver.Instance, |
|||
PixelColorBlendingMode.Darken => DarkenSrcOver.Instance, |
|||
PixelColorBlendingMode.Lighten => LightenSrcOver.Instance, |
|||
PixelColorBlendingMode.Overlay => OverlaySrcOver.Instance, |
|||
PixelColorBlendingMode.HardLight => HardLightSrcOver.Instance, |
|||
_ => NormalSrcOver.Instance, |
|||
}, |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
/// <summary>
|
|||
/// Provides the vector representation used to blend pixels that store associated alpha.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The associated-alpha pixel format.</typeparam>
|
|||
internal abstract class AssociatedAlphaPixelBlender<TPixel> : PixelBlender<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private static readonly PixelOperations<TPixel> Operations = PixelOperations<TPixel>.Instance; |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToBlendVector4<TPixelSource>( |
|||
Configuration configuration, |
|||
ReadOnlySpan<TPixelSource> source, |
|||
Span<Vector4> destination) |
|||
{ |
|||
// Selecting the source representation once per row avoids a format check for every blended pixel.
|
|||
PixelOperations<TPixelSource>.Instance.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override Vector4 ToBlendVector4(TPixel source) => source.ToAssociatedScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromBlendVector4( |
|||
Configuration configuration, |
|||
Span<Vector4> source, |
|||
Span<TPixel> destination) |
|||
{ |
|||
Operations.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,138 @@ |
|||
<# |
|||
// Copyright (c) Six Labors. |
|||
// Licensed under the Six Labors Split License. |
|||
#> |
|||
<#@ template debug="false" hostspecific="false" language="C#" #> |
|||
<#@ assembly name="System.Core" #> |
|||
<#@ output extension=".cs" #> |
|||
// Copyright (c) Six Labors. |
|||
// Licensed under the Six Labors Split License. |
|||
|
|||
// <auto-generated /> |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
internal static partial class AssociatedAlphaPorterDuffFunctions |
|||
{ |
|||
<# |
|||
foreach (string composer in Composers) |
|||
{ |
|||
foreach (string blender in Blenders) |
|||
{ |
|||
string function = blender + composer; |
|||
|
|||
foreach (string vectorType in VectorTypes) |
|||
{ |
|||
string opacityType = vectorType == "Vector4" ? "float" : vectorType; |
|||
string zero = vectorType == "Vector4" ? "Vector4.Zero" : vectorType.Replace("<float>", "<float>.Zero"); |
|||
#> |
|||
/// <summary> |
|||
/// Returns the associated-alpha result of the "<#= function #>" compositing equation. |
|||
/// </summary> |
|||
/// <param name="backdrop">The associated backdrop vector.</param> |
|||
/// <param name="source">The associated source vector.</param> |
|||
/// <param name="opacity">The source opacity in the range 0 through 1.</param> |
|||
/// <returns>The associated composition result.</returns> |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static <#= vectorType #> <#= function #>(<#= vectorType #> backdrop, <#= vectorType #> source, <#= opacityType #> opacity) |
|||
{ |
|||
<# |
|||
if (composer != "Dest" && composer != "Clear") |
|||
{ |
|||
#> |
|||
// Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. |
|||
source *= opacity; |
|||
|
|||
<# |
|||
} |
|||
#> |
|||
return <#= GetComposition(blender, composer, zero) #>; |
|||
} |
|||
|
|||
<# |
|||
} |
|||
} |
|||
} |
|||
#> |
|||
} |
|||
<#+ |
|||
private static readonly string[] Composers = |
|||
{ |
|||
"Src", |
|||
"SrcAtop", |
|||
"SrcOver", |
|||
"SrcIn", |
|||
"SrcOut", |
|||
"Dest", |
|||
"DestAtop", |
|||
"DestOver", |
|||
"DestIn", |
|||
"DestOut", |
|||
"Clear", |
|||
"Xor", |
|||
}; |
|||
|
|||
private static readonly string[] Blenders = |
|||
{ |
|||
"Normal", |
|||
"Multiply", |
|||
"Add", |
|||
"Subtract", |
|||
"Screen", |
|||
"Darken", |
|||
"Lighten", |
|||
"Overlay", |
|||
"HardLight", |
|||
}; |
|||
|
|||
private static readonly string[] VectorTypes = |
|||
{ |
|||
"Vector4", |
|||
"Vector256<float>", |
|||
"Vector512<float>", |
|||
}; |
|||
|
|||
private static string GetComposition(string blender, string composer, string zero) |
|||
{ |
|||
bool normal = blender == "Normal"; |
|||
|
|||
switch (composer) |
|||
{ |
|||
case "Src": |
|||
return "source"; |
|||
case "SrcAtop": |
|||
return normal |
|||
? "AtopNormal(backdrop, source)" |
|||
: $"Atop(backdrop, source, {blender}(backdrop, source))"; |
|||
case "SrcOver": |
|||
return normal |
|||
? "OverNormal(backdrop, source)" |
|||
: $"Over(backdrop, source, {blender}(backdrop, source))"; |
|||
case "SrcIn": |
|||
return "In(backdrop, source)"; |
|||
case "SrcOut": |
|||
return "Out(backdrop, source)"; |
|||
case "Dest": |
|||
return "backdrop"; |
|||
case "DestAtop": |
|||
return normal |
|||
? "AtopNormal(source, backdrop)" |
|||
: $"Atop(source, backdrop, {blender}(source, backdrop))"; |
|||
case "DestOver": |
|||
return normal |
|||
? "OverNormal(source, backdrop)" |
|||
: $"Over(source, backdrop, {blender}(source, backdrop))"; |
|||
case "DestIn": |
|||
return "In(source, backdrop)"; |
|||
case "DestOut": |
|||
return "Out(source, backdrop)"; |
|||
case "Clear": |
|||
return zero; |
|||
default: |
|||
return "Xor(backdrop, source)"; |
|||
} |
|||
} |
|||
#> |
|||
@ -0,0 +1,550 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using SixLabors.ImageSharp.Common.Helpers; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
|
|||
/// <summary>
|
|||
/// Provides Porter-Duff composition functions for associated-alpha vectors.
|
|||
/// </summary>
|
|||
internal static partial class AssociatedAlphaPorterDuffFunctions |
|||
{ |
|||
private const int BlendAlphaControl = 0b_10_00_10_00; |
|||
private const int ShuffleAlphaControl = 0b_11_11_11_11; |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Multiply blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Multiply(Vector4 backdrop, Vector4 source) => backdrop * source; |
|||
|
|||
/// <inheritdoc cref="Multiply(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Multiply(Vector256<float> backdrop, Vector256<float> source) => backdrop * source; |
|||
|
|||
/// <inheritdoc cref="Multiply(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Multiply(Vector512<float> backdrop, Vector512<float> source) => backdrop * source; |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Add blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Add(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return Vector4.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Add(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Add(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return Vector256.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Add(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Add(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return Vector512.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Subtract blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Subtract(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return Vector4.Max(Vector4.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Subtract(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Subtract(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return Vector256.Max(Vector256<float>.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Subtract(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Subtract(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return Vector512.Max(Vector512<float>.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Screen blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Screen(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Screen(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Screen(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Screen(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Screen(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Darken blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Darken(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return Vector4.Min(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Darken(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Darken(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return Vector256.Min(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Darken(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Darken(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return Vector512.Min(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Lighten blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Lighten(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return Vector4.Max(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Lighten(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Lighten(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return Vector256.Max(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Lighten(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Lighten(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return Vector512.Max(backdrop * sourceAlpha, source * backdropAlpha); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for Overlay blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Overlay(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
|
|||
return new Vector4( |
|||
OverlayValue(backdrop.X, backdropAlpha.X, source.X, sourceAlpha.X), |
|||
OverlayValue(backdrop.Y, backdropAlpha.Y, source.Y, sourceAlpha.Y), |
|||
OverlayValue(backdrop.Z, backdropAlpha.Z, source.Z, sourceAlpha.Z), |
|||
0F); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Overlay(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Overlay(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> left = (backdrop + backdrop) * source; |
|||
Vector256<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); |
|||
Vector256<float> useRight = Avx.CompareGreaterThan(backdrop + backdrop, backdropAlpha); |
|||
return Avx.BlendVariable(left, right, useRight); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Overlay(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Overlay(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> left = (backdrop + backdrop) * source; |
|||
Vector512<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); |
|||
Vector512<float> useRight = Avx512F.CompareGreaterThan(backdrop + backdrop, backdropAlpha); |
|||
return Vector512.ConditionalSelect(useRight, right, left); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the associated overlap term for HardLight blending.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated overlap term.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 HardLight(Vector4 backdrop, Vector4 source) |
|||
{ |
|||
Vector4 backdropAlpha = Numerics.PermuteW(backdrop); |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
|
|||
return new Vector4( |
|||
OverlayValue(source.X, sourceAlpha.X, backdrop.X, backdropAlpha.X), |
|||
OverlayValue(source.Y, sourceAlpha.Y, backdrop.Y, backdropAlpha.Y), |
|||
OverlayValue(source.Z, sourceAlpha.Z, backdrop.Z, backdropAlpha.Z), |
|||
0F); |
|||
} |
|||
|
|||
/// <inheritdoc cref="HardLight(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> HardLight(Vector256<float> backdrop, Vector256<float> source) |
|||
{ |
|||
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> left = (backdrop + backdrop) * source; |
|||
Vector256<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); |
|||
Vector256<float> useRight = Avx.CompareGreaterThan(source + source, sourceAlpha); |
|||
return Avx.BlendVariable(left, right, useRight); |
|||
} |
|||
|
|||
/// <inheritdoc cref="HardLight(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> HardLight(Vector512<float> backdrop, Vector512<float> source) |
|||
{ |
|||
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> left = (backdrop + backdrop) * source; |
|||
Vector512<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); |
|||
Vector512<float> useRight = Avx512F.CompareGreaterThan(source + source, sourceAlpha); |
|||
return Vector512.ConditionalSelect(useRight, right, left); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Composites an associated source over an associated destination without a color-blending function.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 OverNormal(Vector4 destination, Vector4 source) |
|||
{ |
|||
// Associated source-over is Ps + Pb(1 - As); both color and alpha therefore use the same coefficient.
|
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
return source + (destination * (Vector4.One - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="OverNormal(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> OverNormal(Vector256<float> destination, Vector256<float> source) |
|||
{ |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
return source + (destination * (Vector256.Create(1F) - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="OverNormal(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> OverNormal(Vector512<float> destination, Vector512<float> source) |
|||
{ |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
return source + (destination * (Vector512.Create(1F) - sourceAlpha)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Composites an associated source atop an associated destination without a color-blending function.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 AtopNormal(Vector4 destination, Vector4 source) |
|||
{ |
|||
// Source-atop retains the destination alpha while replacing its covered contribution with the source.
|
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
Vector4 destinationAlpha = Numerics.PermuteW(destination); |
|||
return (source * destinationAlpha) + (destination * (Vector4.One - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="AtopNormal(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> AtopNormal(Vector256<float> destination, Vector256<float> source) |
|||
{ |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); |
|||
return (source * destinationAlpha) + (destination * (Vector256.Create(1F) - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="AtopNormal(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> AtopNormal(Vector512<float> destination, Vector512<float> source) |
|||
{ |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); |
|||
return (source * destinationAlpha) + (destination * (Vector512.Create(1F) - sourceAlpha)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Composites an associated source over an associated destination using an unassociated blended color.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <param name="overlap">The associated overlap term produced by the color-blending function.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 overlap) |
|||
{ |
|||
// The three terms cover destination-only, source-only, and overlapping color respectively.
|
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
Vector4 destinationAlpha = Numerics.PermuteW(destination); |
|||
Vector4 result = (destination * (Vector4.One - sourceAlpha)) + (source * (Vector4.One - destinationAlpha)) + overlap; |
|||
Vector4 alpha = source + (destination * (Vector4.One - sourceAlpha)); |
|||
return Numerics.WithW(result, alpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Over(Vector4, Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Over(Vector256<float> destination, Vector256<float> source, Vector256<float> overlap) |
|||
{ |
|||
Vector256<float> one = Vector256.Create(1F); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); |
|||
Vector256<float> result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; |
|||
Vector256<float> alpha = source + (destination * (one - sourceAlpha)); |
|||
return Avx.Blend(result, alpha, BlendAlphaControl); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Over(Vector4, Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Over(Vector512<float> destination, Vector512<float> source, Vector512<float> overlap) |
|||
{ |
|||
Vector512<float> one = Vector512.Create(1F); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); |
|||
Vector512<float> result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; |
|||
Vector512<float> alpha = source + (destination * (one - sourceAlpha)); |
|||
return Vector512.ConditionalSelect(AlphaMask512(), alpha, result); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Composites an associated source atop an associated destination using an unassociated blended color.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <param name="overlap">The associated overlap term produced by the color-blending function.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 overlap) |
|||
{ |
|||
// Atop discards source-only color and retains the destination alpha unchanged.
|
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
Vector4 destinationAlpha = Numerics.PermuteW(destination); |
|||
Vector4 coefficient = Vector4.One - sourceAlpha; |
|||
Vector4 result = Vector128_.FusedMultiplyAdd(destination.AsVector128(), coefficient.AsVector128(), overlap.AsVector128()).AsVector4(); |
|||
|
|||
return Numerics.WithW(result, destinationAlpha); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Atop(Vector4, Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Atop(Vector256<float> destination, Vector256<float> source, Vector256<float> overlap) |
|||
{ |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); |
|||
Vector256<float> result = Vector256_.FusedMultiplyAdd(destination, Vector256.Create(1F) - sourceAlpha, overlap); |
|||
|
|||
return Avx.Blend(result, destinationAlpha, BlendAlphaControl); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Atop(Vector4, Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Atop(Vector512<float> destination, Vector512<float> source, Vector512<float> overlap) |
|||
{ |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); |
|||
Vector512<float> result = Vector512_.FusedMultiplyAdd(destination, Vector512.Create(1F) - sourceAlpha, overlap); |
|||
|
|||
return Vector512.ConditionalSelect(AlphaMask512(), destinationAlpha, result); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retains the associated source within the destination coverage.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 In(Vector4 destination, Vector4 source) => source * Numerics.PermuteW(destination); |
|||
|
|||
/// <inheritdoc cref="In(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> In(Vector256<float> destination, Vector256<float> source) |
|||
=> source * Avx.Permute(destination, ShuffleAlphaControl); |
|||
|
|||
/// <inheritdoc cref="In(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> In(Vector512<float> destination, Vector512<float> source) |
|||
=> source * Vector512_.ShuffleNative(destination, ShuffleAlphaControl); |
|||
|
|||
/// <summary>
|
|||
/// Retains the associated source outside the destination coverage.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Out(Vector4 destination, Vector4 source) |
|||
=> source * (Vector4.One - Numerics.PermuteW(destination)); |
|||
|
|||
/// <inheritdoc cref="Out(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Out(Vector256<float> destination, Vector256<float> source) |
|||
=> source * (Vector256.Create(1F) - Avx.Permute(destination, ShuffleAlphaControl)); |
|||
|
|||
/// <inheritdoc cref="Out(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Out(Vector512<float> destination, Vector512<float> source) |
|||
=> source * (Vector512.Create(1F) - Vector512_.ShuffleNative(destination, ShuffleAlphaControl)); |
|||
|
|||
/// <summary>
|
|||
/// Retains only the non-overlapping parts of two associated vectors.
|
|||
/// </summary>
|
|||
/// <param name="destination">The associated destination vector.</param>
|
|||
/// <param name="source">The associated source vector.</param>
|
|||
/// <returns>The associated composition result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Xor(Vector4 destination, Vector4 source) |
|||
{ |
|||
Vector4 sourceAlpha = Numerics.PermuteW(source); |
|||
Vector4 destinationAlpha = Numerics.PermuteW(destination); |
|||
return (source * (Vector4.One - destinationAlpha)) + (destination * (Vector4.One - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Xor(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Xor(Vector256<float> destination, Vector256<float> source) |
|||
{ |
|||
Vector256<float> one = Vector256.Create(1F); |
|||
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); |
|||
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); |
|||
return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); |
|||
} |
|||
|
|||
/// <inheritdoc cref="Xor(Vector4, Vector4)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> Xor(Vector512<float> destination, Vector512<float> source) |
|||
{ |
|||
Vector512<float> one = Vector512.Create(1F); |
|||
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); |
|||
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); |
|||
return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies raster coverage to an associated composition result.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop vector.</param>
|
|||
/// <param name="source">The associated composition result.</param>
|
|||
/// <param name="coverage">The raster coverage in the range 0 through 1.</param>
|
|||
/// <returns>The covered associated result.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 BlendWithCoverage(Vector4 backdrop, Vector4 source, float coverage) |
|||
{ |
|||
// Use the same fused operation as the wider paths so exact midpoints cannot change across vector widths.
|
|||
return Vector128_.FusedMultiplyAdd((source - backdrop).AsVector128(), Vector128.Create(coverage), backdrop.AsVector128()).AsVector4(); |
|||
} |
|||
|
|||
/// <inheritdoc cref="BlendWithCoverage(Vector4, Vector4, float)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> BlendWithCoverage(Vector256<float> backdrop, Vector256<float> source, Vector256<float> coverage) |
|||
=> Vector256_.FusedMultiplyAdd(source - backdrop, coverage, backdrop); |
|||
|
|||
/// <inheritdoc cref="BlendWithCoverage(Vector4, Vector4, float)" />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector512<float> BlendWithCoverage(Vector512<float> backdrop, Vector512<float> source, Vector512<float> coverage) |
|||
=> Vector512_.FusedMultiplyAdd(source - backdrop, coverage, backdrop); |
|||
|
|||
/// <summary>
|
|||
/// Calculates one associated Overlay overlap component without recovering either straight component.
|
|||
/// </summary>
|
|||
/// <param name="backdrop">The associated backdrop component.</param>
|
|||
/// <param name="backdropAlpha">The backdrop alpha.</param>
|
|||
/// <param name="source">The associated source component.</param>
|
|||
/// <param name="sourceAlpha">The source alpha.</param>
|
|||
/// <returns>The associated overlap component.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float OverlayValue(float backdrop, float backdropAlpha, float source, float sourceAlpha) |
|||
{ |
|||
// Comparing 2Pb with Ab is equivalent to comparing the straight backdrop component with one half.
|
|||
return (backdrop + backdrop) <= backdropAlpha |
|||
? (backdrop + backdrop) * source |
|||
: (backdropAlpha * sourceAlpha) - (2F * (backdropAlpha - backdrop) * (sourceAlpha - source)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a SIMD lane mask selecting the alpha component of each packed vector.
|
|||
/// </summary>
|
|||
/// <returns>The alpha-component mask.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector512<float> AlphaMask512() |
|||
=> Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); |
|||
} |
|||
@ -0,0 +1,273 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.PixelFormats.Utils; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Packed pixel type containing alpha and associated blue, green, and red components as 8-bit unsigned normalized values.
|
|||
/// Components are stored in alpha, blue, green, and red order from least to most significant byte.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The native component, packed, and vector representations use associated alpha.
|
|||
/// </remarks>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public partial struct Abgr32P : IPixel<Abgr32P>, IPackedVector<uint> |
|||
{ |
|||
private static readonly Vector4 Half = new(0.5F); |
|||
private static readonly Vector4 MaxBytes = new(byte.MaxValue); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the alpha component.
|
|||
/// </summary>
|
|||
public byte A; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated blue component.
|
|||
/// </summary>
|
|||
public byte B; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated green component.
|
|||
/// </summary>
|
|||
public byte G; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated red component.
|
|||
/// </summary>
|
|||
public byte R; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Abgr32P(byte r, byte g, byte b) |
|||
: this(r, g, b, byte.MaxValue) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Abgr32P(byte r, byte g, byte b, byte a) |
|||
{ |
|||
this.A = a; |
|||
this.B = b; |
|||
this.G = g; |
|||
this.R = r; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Abgr32P(float r, float g, float b) |
|||
: this(new Vector4(r, g, b, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Abgr32P(float r, float g, float b, float a) |
|||
: this(new Vector4(r, g, b, a)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Abgr32P(Vector3 vector) |
|||
: this(new Vector4(vector, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Abgr32P(Vector4 vector) |
|||
: this() => this = FromScaledVector4(vector); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from a packed associated value.
|
|||
/// </summary>
|
|||
/// <param name="packed">The packed associated value.</param>
|
|||
public Abgr32P(uint packed) |
|||
: this() => this.PackedValue = packed; |
|||
|
|||
/// <inheritdoc />
|
|||
public uint PackedValue |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
readonly get => Unsafe.As<Abgr32P, uint>(ref Unsafe.AsRef(in this)); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set => Unsafe.As<Abgr32P, uint>(ref this) = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Abgr32P"/> values for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are equal.</returns>
|
|||
public static bool operator ==(Abgr32P left, Abgr32P right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Abgr32P"/> values for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
|||
public static bool operator !=(Abgr32P left, Abgr32P right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Rgba32 ToRgba32() |
|||
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedScaledVector4() |
|||
{ |
|||
// Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelTypeInfo GetPixelTypeInfo() |
|||
=> PixelTypeInfo.Create<Abgr32P>( |
|||
PixelComponentInfo.Create<Abgr32P>(4, 8, 8, 8, 8), |
|||
PixelColorType.Alpha | PixelColorType.BGR, |
|||
PixelAlphaRepresentation.Associated); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelOperations<Abgr32P> CreatePixelOperations() => new PixelOperations(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Abgr32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromVector4(Vector4 source) => FromAssociatedVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Abgr32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Abgr32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Abgr32P FromUnassociatedScaledVector4(Vector4 source) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToAbgr32P(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Abgr32P FromAssociatedScaledVector4(Vector4 source) |
|||
{ |
|||
// Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToAbgr32P(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Abgr32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly bool Equals(object? obj) => obj is Abgr32P other && this.Equals(other); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly bool Equals(Abgr32P other) => this.PackedValue.Equals(other.PackedValue); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly string ToString() => $"Abgr32P({this.R}, {this.G}, {this.B}, {this.A})"; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Abgr32P Pack(Vector4 vector) |
|||
{ |
|||
vector *= MaxBytes; |
|||
vector += Half; |
|||
vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); |
|||
|
|||
// Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12.
|
|||
Vector128<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); |
|||
return new Abgr32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); |
|||
} |
|||
} |
|||
@ -0,0 +1,273 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.PixelFormats.Utils; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Packed pixel type containing alpha and associated red, green, and blue components as 8-bit unsigned normalized values.
|
|||
/// Components are stored in alpha, red, green, and blue order from least to most significant byte.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The native component, packed, and vector representations use associated alpha.
|
|||
/// </remarks>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public partial struct Argb32P : IPixel<Argb32P>, IPackedVector<uint> |
|||
{ |
|||
private static readonly Vector4 Half = new(0.5F); |
|||
private static readonly Vector4 MaxBytes = new(byte.MaxValue); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the alpha component.
|
|||
/// </summary>
|
|||
public byte A; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated red component.
|
|||
/// </summary>
|
|||
public byte R; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated green component.
|
|||
/// </summary>
|
|||
public byte G; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated blue component.
|
|||
/// </summary>
|
|||
public byte B; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Argb32P(byte r, byte g, byte b) |
|||
: this(r, g, b, byte.MaxValue) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Argb32P(byte r, byte g, byte b, byte a) |
|||
{ |
|||
this.A = a; |
|||
this.R = r; |
|||
this.G = g; |
|||
this.B = b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Argb32P(float r, float g, float b) |
|||
: this(new Vector4(r, g, b, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Argb32P(float r, float g, float b, float a) |
|||
: this(new Vector4(r, g, b, a)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Argb32P(Vector3 vector) |
|||
: this(new Vector4(vector, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Argb32P(Vector4 vector) |
|||
: this() => this = FromScaledVector4(vector); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Argb32P"/> struct from a packed associated value.
|
|||
/// </summary>
|
|||
/// <param name="packed">The packed associated value.</param>
|
|||
public Argb32P(uint packed) |
|||
: this() => this.PackedValue = packed; |
|||
|
|||
/// <inheritdoc />
|
|||
public uint PackedValue |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
readonly get => Unsafe.As<Argb32P, uint>(ref Unsafe.AsRef(in this)); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set => Unsafe.As<Argb32P, uint>(ref this) = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Argb32P"/> values for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are equal.</returns>
|
|||
public static bool operator ==(Argb32P left, Argb32P right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Argb32P"/> values for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
|||
public static bool operator !=(Argb32P left, Argb32P right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Rgba32 ToRgba32() |
|||
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedScaledVector4() |
|||
{ |
|||
// Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelTypeInfo GetPixelTypeInfo() |
|||
=> PixelTypeInfo.Create<Argb32P>( |
|||
PixelComponentInfo.Create<Argb32P>(4, 8, 8, 8, 8), |
|||
PixelColorType.Alpha | PixelColorType.RGB, |
|||
PixelAlphaRepresentation.Associated); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelOperations<Argb32P> CreatePixelOperations() => new PixelOperations(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Argb32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromVector4(Vector4 source) => FromAssociatedVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Argb32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Argb32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Argb32P FromUnassociatedScaledVector4(Vector4 source) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToArgb32P(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Argb32P FromAssociatedScaledVector4(Vector4 source) |
|||
{ |
|||
// Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToArgb32P(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Argb32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly bool Equals(object? obj) => obj is Argb32P other && this.Equals(other); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly bool Equals(Argb32P other) => this.PackedValue.Equals(other.PackedValue); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly string ToString() => $"Argb32P({this.R}, {this.G}, {this.B}, {this.A})"; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Argb32P Pack(Vector4 vector) |
|||
{ |
|||
vector *= MaxBytes; |
|||
vector += Half; |
|||
vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); |
|||
|
|||
// Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12.
|
|||
Vector128<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); |
|||
return new Argb32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); |
|||
} |
|||
} |
|||
@ -0,0 +1,273 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.PixelFormats.Utils; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Packed pixel type containing associated blue, green, red, and alpha components as 8-bit unsigned normalized values.
|
|||
/// Components are stored in blue, green, red, and alpha order from least to most significant byte.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The native component, packed, and vector representations use associated alpha.
|
|||
/// </remarks>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
public partial struct Bgra32P : IPixel<Bgra32P>, IPackedVector<uint> |
|||
{ |
|||
private static readonly Vector4 Half = new(0.5F); |
|||
private static readonly Vector4 MaxBytes = new(byte.MaxValue); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated blue component.
|
|||
/// </summary>
|
|||
public byte B; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated green component.
|
|||
/// </summary>
|
|||
public byte G; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the associated red component.
|
|||
/// </summary>
|
|||
public byte R; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the alpha component.
|
|||
/// </summary>
|
|||
public byte A; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Bgra32P(byte r, byte g, byte b) |
|||
: this(r, g, b, byte.MaxValue) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Bgra32P(byte r, byte g, byte b, byte a) |
|||
{ |
|||
this.B = b; |
|||
this.G = g; |
|||
this.R = r; |
|||
this.A = a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
public Bgra32P(float r, float g, float b) |
|||
: this(new Vector4(r, g, b, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
|
|||
/// </summary>
|
|||
/// <param name="r">The associated red component.</param>
|
|||
/// <param name="g">The associated green component.</param>
|
|||
/// <param name="b">The associated blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Bgra32P(float r, float g, float b, float a) |
|||
: this(new Vector4(r, g, b, a)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Bgra32P(Vector3 vector) |
|||
: this(new Vector4(vector, 1F)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from an associated vector.
|
|||
/// </summary>
|
|||
/// <param name="vector">The associated vector.</param>
|
|||
public Bgra32P(Vector4 vector) |
|||
: this() => this = FromScaledVector4(vector); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from a packed associated value.
|
|||
/// </summary>
|
|||
/// <param name="packed">The packed associated value.</param>
|
|||
public Bgra32P(uint packed) |
|||
: this() => this.PackedValue = packed; |
|||
|
|||
/// <inheritdoc />
|
|||
public uint PackedValue |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
readonly get => Unsafe.As<Bgra32P, uint>(ref Unsafe.AsRef(in this)); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set => Unsafe.As<Bgra32P, uint>(ref this) = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Bgra32P"/> values for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are equal.</returns>
|
|||
public static bool operator ==(Bgra32P left, Bgra32P right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Bgra32P"/> values for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
|||
public static bool operator !=(Bgra32P left, Bgra32P right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Rgba32 ToRgba32() |
|||
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedScaledVector4() |
|||
{ |
|||
// Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelTypeInfo GetPixelTypeInfo() |
|||
=> PixelTypeInfo.Create<Bgra32P>( |
|||
PixelComponentInfo.Create<Bgra32P>(4, 8, 8, 8, 8), |
|||
PixelColorType.BGR | PixelColorType.Alpha, |
|||
PixelAlphaRepresentation.Associated); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelOperations<Bgra32P> CreatePixelOperations() => new PixelOperations(); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Bgra32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromVector4(Vector4 source) => FromAssociatedVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Bgra32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Bgra32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Bgra32P FromUnassociatedScaledVector4(Vector4 source) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToBgra32P(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Bgra32P FromAssociatedScaledVector4(Vector4 source) |
|||
{ |
|||
// Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written.
|
|||
return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToBgra32P(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static Bgra32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly bool Equals(object? obj) => obj is Bgra32P other && this.Equals(other); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly bool Equals(Bgra32P other) => this.PackedValue.Equals(other.PackedValue); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly string ToString() => $"Bgra32P({this.R}, {this.G}, {this.B}, {this.A})"; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Bgra32P Pack(Vector4 vector) |
|||
{ |
|||
vector *= MaxBytes; |
|||
vector += Half; |
|||
vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); |
|||
|
|||
// Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12.
|
|||
Vector128<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); |
|||
return new Bgra32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); |
|||
} |
|||
} |
|||
@ -0,0 +1,270 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Packed pixel type containing four associated 16-bit floating-point values.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <see cref="ToVector4"/> returns the stored associated IEEE 754 binary16 values directly. Scaled vector conversions
|
|||
/// normalize the finite range <c>[-65504, 65504]</c> to <c>[0, 1]</c> while preserving associated alpha. The packed
|
|||
/// representation is binary-compatible with <c>DXGI_FORMAT_R16G16B16A16_FLOAT</c>.
|
|||
/// </remarks>
|
|||
public partial struct HalfVector4P : IPixel<HalfVector4P>, IPackedVector<ulong> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HalfVector4P"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="x">The associated x-component.</param>
|
|||
/// <param name="y">The associated y-component.</param>
|
|||
/// <param name="z">The associated z-component.</param>
|
|||
/// <param name="w">The alpha component.</param>
|
|||
public HalfVector4P(float x, float y, float z, float w) |
|||
: this(new Vector4(x, y, z, w)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HalfVector4P"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector containing the associated component values.</param>
|
|||
public HalfVector4P(Vector4 vector) => this.PackedValue = Pack(vector); |
|||
|
|||
/// <inheritdoc />
|
|||
public ulong PackedValue { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="HalfVector4P"/> values for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are equal.</returns>
|
|||
public static bool operator ==(HalfVector4P left, HalfVector4P right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="HalfVector4P"/> values for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
|||
public static bool operator !=(HalfVector4P left, HalfVector4P right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Rgba32 ToRgba32() |
|||
{ |
|||
return Rgba32.FromScaledVector4(this.ToUnassociatedScaledVector4()); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToScaledVector4() => HalfTypeHelper.ToScaled(this.ToVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedScaledVector4() |
|||
{ |
|||
Vector4 vector = this.ToScaledVector4(); |
|||
Numerics.UnPremultiply(ref vector); |
|||
return vector; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToVector4() => new( |
|||
HalfTypeHelper.Unpack((ushort)this.PackedValue), |
|||
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), |
|||
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), |
|||
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedVector4() |
|||
{ |
|||
Vector4 vector = this.ToUnassociatedScaledVector4(); |
|||
return HalfTypeHelper.FromScaled(vector); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelTypeInfo GetPixelTypeInfo() |
|||
=> PixelTypeInfo.Create<HalfVector4P>( |
|||
PixelComponentInfo.Create<HalfVector4P>(4, 16, 16, 16, 16), |
|||
PixelColorType.RGB | PixelColorType.Alpha, |
|||
PixelAlphaRepresentation.Associated); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelOperations<HalfVector4P> CreatePixelOperations() => new PixelOperations(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromVector4(Vector4 source) => FromAssociatedVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static HalfVector4P FromUnassociatedVector4(Vector4 source) |
|||
{ |
|||
source = HalfTypeHelper.ToScaled(source); |
|||
return FromUnassociatedScaledVector4(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static HalfVector4P FromAssociatedVector4(Vector4 source) |
|||
{ |
|||
source = HalfTypeHelper.ToScaled(source); |
|||
return FromAssociatedScaledVector4(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static HalfVector4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source)); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static HalfVector4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source)); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static HalfVector4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly bool Equals(object? obj) => obj is HalfVector4P other && this.Equals(other); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly bool Equals(HalfVector4P other) => this.PackedValue.Equals(other.PackedValue); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly string ToString() |
|||
{ |
|||
Vector4 vector = this.ToVector4(); |
|||
return FormattableString.Invariant($"HalfVector4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts an unassociated scaled vector to the associated representation of a half-precision destination.
|
|||
/// </summary>
|
|||
/// <param name="source">The unassociated scaled vector.</param>
|
|||
/// <returns>The associated scaled vector.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 Associate(Vector4 source) |
|||
{ |
|||
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); |
|||
|
|||
// RGB must use the scaled alpha that the binary16 representation can reproduce.
|
|||
source.W = QuantizeScaledAlpha(source.W); |
|||
Numerics.Premultiply(ref source); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reassociates a scaled vector with the alpha value the destination stores.
|
|||
/// </summary>
|
|||
/// <param name="source">The associated scaled vector.</param>
|
|||
/// <returns>The reassociated scaled vector.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 Reassociate(Vector4 source) |
|||
{ |
|||
float alpha = source.W; |
|||
|
|||
if (alpha <= 0) |
|||
{ |
|||
return Vector4.Zero; |
|||
} |
|||
|
|||
float storedAlpha = QuantizeScaledAlpha(alpha); |
|||
|
|||
// Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization.
|
|||
source *= storedAlpha / alpha; |
|||
source.W = storedAlpha; |
|||
Numerics.ClampRgbToAlpha(ref source); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Packs an associated scaled vector into the native binary16 representation.
|
|||
/// </summary>
|
|||
/// <param name="source">The associated scaled vector.</param>
|
|||
/// <returns>The packed pixel.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static HalfVector4P PackAssociatedScaledVector4(Vector4 source) |
|||
{ |
|||
source = HalfTypeHelper.FromScaled(source); |
|||
return new HalfVector4P { PackedValue = Pack(source) }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Quantizes scaled alpha through the native binary16 representation.
|
|||
/// </summary>
|
|||
/// <param name="alpha">The scaled alpha value.</param>
|
|||
/// <returns>The scaled value represented by the stored binary16 component.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float QuantizeScaledAlpha(float alpha) |
|||
{ |
|||
float nativeAlpha = HalfTypeHelper.FromScaled(Numerics.Clamp(alpha, 0F, 1F)); |
|||
return HalfTypeHelper.ToScaled(HalfTypeHelper.Unpack(HalfTypeHelper.Pack(nativeAlpha))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Packs native half-precision components into a 64-bit value.
|
|||
/// </summary>
|
|||
/// <param name="vector">The native component values.</param>
|
|||
/// <returns>The packed value.</returns>
|
|||
private static ulong Pack(Vector4 vector) |
|||
{ |
|||
ulong x = HalfTypeHelper.Pack(vector.X); |
|||
ulong y = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; |
|||
ulong z = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20; |
|||
ulong w = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; |
|||
return x | y | z | w; |
|||
} |
|||
} |
|||
@ -0,0 +1,318 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Packed pixel type containing four associated 8-bit signed normalized values ranging from -1 to 1.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <see cref="ToVector4"/> returns associated components in the native signed-normalized range <c>[-1, 1]</c>.
|
|||
/// Scaled vector conversions return associated components in <c>[0, 1]</c>.
|
|||
/// The packed two's-complement codes <c>-128</c> and <c>-127</c> both represent <c>-1</c>,
|
|||
/// matching <c>DXGI_FORMAT_R8G8B8A8_SNORM</c>.
|
|||
/// </remarks>
|
|||
public partial struct NormalizedByte4P : IPixel<NormalizedByte4P>, IPackedVector<uint> |
|||
{ |
|||
private const float MaxPos = 127F; |
|||
private const float ScaledMagnitude = MaxPos * 2F; |
|||
private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); |
|||
private static readonly Vector4 Minimum = -Half; |
|||
private static readonly Vector4 MinusOne = Vector128.Create(-1F).AsVector4(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NormalizedByte4P"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="x">The associated x-component.</param>
|
|||
/// <param name="y">The associated y-component.</param>
|
|||
/// <param name="z">The associated z-component.</param>
|
|||
/// <param name="w">The alpha component.</param>
|
|||
public NormalizedByte4P(float x, float y, float z, float w) |
|||
: this(new Vector4(x, y, z, w)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NormalizedByte4P"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector containing the associated component values.</param>
|
|||
public NormalizedByte4P(Vector4 vector) => this.PackedValue = Pack(vector); |
|||
|
|||
/// <inheritdoc />
|
|||
public uint PackedValue { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="NormalizedByte4P"/> values for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are equal.</returns>
|
|||
public static bool operator ==(NormalizedByte4P left, NormalizedByte4P right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="NormalizedByte4P"/> values for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The left value.</param>
|
|||
/// <param name="right">The right value.</param>
|
|||
/// <returns><see langword="true"/> when the values are not equal.</returns>
|
|||
public static bool operator !=(NormalizedByte4P left, NormalizedByte4P right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(ToUnassociatedScaledVector4(this)); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToScaledVector4() |
|||
{ |
|||
// Offset the exact signed components before division. Mapping an already normalized value through (value + 1) / 2 loses precision near -1 through cancellation.
|
|||
Vector4 scaled = new( |
|||
(sbyte)((this.PackedValue >> 0) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 8) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 16) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 24) & 0xFF)); |
|||
|
|||
// SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -128 cannot escape the scaled range.
|
|||
return (Vector4.Max(scaled, Minimum) + Half) / ScaledMagnitude; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedScaledVector4() => ToUnassociatedScaledVector4(this); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly Vector4 ToVector4() |
|||
{ |
|||
Vector4 vector = new( |
|||
(sbyte)((this.PackedValue >> 0) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 8) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 16) & 0xFF), |
|||
(sbyte)((this.PackedValue >> 24) & 0xFF)); |
|||
|
|||
// DirectX SNORM maps both -128 and -127 to -1.
|
|||
return Vector4.Max(vector, Minimum) / MaxPos; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToUnassociatedVector4() |
|||
{ |
|||
// Association is defined in the common scaled domain. Signed-native W is an affine encoding of alpha, so it cannot be used as a divisor directly.
|
|||
Vector4 vector = this.ToUnassociatedScaledVector4(); |
|||
vector *= 2F; |
|||
vector -= Vector4.One; |
|||
return vector; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelTypeInfo GetPixelTypeInfo() |
|||
=> PixelTypeInfo.Create<NormalizedByte4P>( |
|||
PixelComponentInfo.Create<NormalizedByte4P>(4, 8, 8, 8, 8), |
|||
PixelColorType.RGB | PixelColorType.Alpha, |
|||
PixelAlphaRepresentation.Associated); |
|||
|
|||
/// <inheritdoc />
|
|||
public static PixelOperations<NormalizedByte4P> CreatePixelOperations() => new PixelOperations(); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromVector4(Vector4 source) => FromAssociatedVector4(source); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static NormalizedByte4P FromUnassociatedVector4(Vector4 source) |
|||
{ |
|||
// Convert the signed-native range to the common scaled domain before associating because native W is not opacity.
|
|||
source += Vector4.One; |
|||
source /= 2F; |
|||
return FromUnassociatedScaledVector4(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static NormalizedByte4P FromAssociatedVector4(Vector4 source) |
|||
{ |
|||
// Reassociation must also operate in scaled space so RGB follows the quantized scaled alpha rather than the signed-native W component.
|
|||
source += Vector4.One; |
|||
source /= 2F; |
|||
return FromAssociatedScaledVector4(source); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static NormalizedByte4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source)); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static NormalizedByte4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source)); |
|||
|
|||
/// <summary>
|
|||
/// Packs an associated scaled vector into signed-normalized storage.
|
|||
/// </summary>
|
|||
/// <param name="source">The associated scaled vector.</param>
|
|||
/// <returns>The packed pixel.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static NormalizedByte4P PackAssociatedScaledVector4(Vector4 source) |
|||
{ |
|||
// Signed-normalized storage uses the native [-1, 1] range even though association is defined in scaled opacity space.
|
|||
source *= 2F; |
|||
source -= Vector4.One; |
|||
return new() { PackedValue = Pack(source) }; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public static NormalizedByte4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly bool Equals(object? obj) => obj is NormalizedByte4P other && this.Equals(other); |
|||
|
|||
/// <inheritdoc />
|
|||
public readonly bool Equals(NormalizedByte4P other) => this.PackedValue.Equals(other.PackedValue); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); |
|||
|
|||
/// <inheritdoc />
|
|||
public override readonly string ToString() |
|||
{ |
|||
Vector4 vector = this.ToVector4(); |
|||
return FormattableString.Invariant($"NormalizedByte4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts an unassociated scaled vector to the associated representation of a signed-normalized-byte destination.
|
|||
/// </summary>
|
|||
/// <param name="source">The unassociated scaled vector.</param>
|
|||
/// <returns>The associated scaled vector.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 Associate(Vector4 source) |
|||
{ |
|||
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); |
|||
|
|||
// Reproduce the signed-normalized packer's alpha quantization, then associate RGB with the exact alpha that will be stored.
|
|||
float nativeAlpha = Numerics.Clamp((source.W * 2F) - 1F, -1F, 1F); |
|||
float storedAlpha = MathF.Round(nativeAlpha * MaxPos); |
|||
source.W = (storedAlpha + MaxPos) / ScaledMagnitude; |
|||
Numerics.Premultiply(ref source); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the stored associated components to an unassociated scaled vector.
|
|||
/// </summary>
|
|||
/// <param name="source">The associated pixel.</param>
|
|||
/// <returns>The unassociated scaled vector.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 ToUnassociatedScaledVector4(NormalizedByte4P source) |
|||
{ |
|||
// Offset signed storage into exact nonnegative byte magnitudes before division so the quotient retains the destination's byte-rounding midpoint.
|
|||
Vector4 vector = new( |
|||
(sbyte)(source.PackedValue >> 0), |
|||
(sbyte)(source.PackedValue >> 8), |
|||
(sbyte)(source.PackedValue >> 16), |
|||
(sbyte)(source.PackedValue >> 24)); |
|||
|
|||
// Clamp the duplicate SNORM minimum encoding before converting it to the nonnegative associated domain.
|
|||
vector = Vector4.Max(vector, Minimum) + Half; |
|||
|
|||
if (vector.W == 0F) |
|||
{ |
|||
// Numerics.UnPremultiply preserves RGB when alpha is zero. Normalize the stored components because they already are the unassociated value in this case.
|
|||
return vector / ScaledMagnitude; |
|||
} |
|||
|
|||
Numerics.UnPremultiply(ref vector); |
|||
vector.W /= ScaledMagnitude; |
|||
return vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reassociates a scaled vector with the alpha value the destination stores.
|
|||
/// </summary>
|
|||
/// <param name="source">The associated scaled vector.</param>
|
|||
/// <returns>The reassociated scaled vector.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 Reassociate(Vector4 source) |
|||
{ |
|||
float alpha = source.W; |
|||
|
|||
if (alpha <= 0) |
|||
{ |
|||
return Vector4.Zero; |
|||
} |
|||
|
|||
float nativeAlpha = Numerics.Clamp((alpha * 2F) - 1F, -1F, 1F); |
|||
float storedAlpha = (MathF.Round(nativeAlpha * MaxPos) + MaxPos) / ScaledMagnitude; |
|||
|
|||
// Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization.
|
|||
source *= storedAlpha / alpha; |
|||
source.W = storedAlpha; |
|||
Numerics.ClampRgbToAlpha(ref source); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Packs native signed normalized components into a 32-bit value.
|
|||
/// </summary>
|
|||
/// <param name="vector">The native component values.</param>
|
|||
/// <returns>The packed value.</returns>
|
|||
private static uint Pack(Vector4 vector) |
|||
{ |
|||
vector = Numerics.Clamp(vector, MinusOne, Vector4.One) * Half; |
|||
|
|||
uint byte4 = ((uint)Convert.ToInt16(MathF.Round(vector.X)) & 0xFF) << 0; |
|||
uint byte3 = ((uint)Convert.ToInt16(MathF.Round(vector.Y)) & 0xFF) << 8; |
|||
uint byte2 = ((uint)Convert.ToInt16(MathF.Round(vector.Z)) & 0xFF) << 16; |
|||
uint byte1 = ((uint)Convert.ToInt16(MathF.Round(vector.W)) & 0xFF) << 24; |
|||
|
|||
return byte4 | byte3 | byte2 | byte1; |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.PixelFormats.Utils; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Abgr32P |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized bulk operations for <see cref="Abgr32P"/>.
|
|||
/// </summary>
|
|||
internal class PixelOperations : AssociatedAlphaPixelOperations<Abgr32P> |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination) |
|||
{ |
|||
// Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts.
|
|||
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination) |
|||
{ |
|||
// The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored.
|
|||
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination) |
|||
{ |
|||
// This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts.
|
|||
Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination) |
|||
{ |
|||
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination) |
|||
{ |
|||
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.PixelFormats.Utils; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Argb32P |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized bulk operations for <see cref="Argb32P"/>.
|
|||
/// </summary>
|
|||
internal class PixelOperations : AssociatedAlphaPixelOperations<Argb32P> |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination) |
|||
{ |
|||
// Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts.
|
|||
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination) |
|||
{ |
|||
// The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored.
|
|||
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination) |
|||
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination) |
|||
{ |
|||
// This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts.
|
|||
Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination) |
|||
{ |
|||
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination) |
|||
{ |
|||
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue